diff options
Diffstat (limited to 'drivers/sound/emu10k1')
33 files changed, 9219 insertions, 0 deletions
diff --git a/drivers/sound/emu10k1/8010.h b/drivers/sound/emu10k1/8010.h new file mode 100644 index 000000000..8f4526013 --- /dev/null +++ b/drivers/sound/emu10k1/8010.h @@ -0,0 +1,662 @@ +/* + ********************************************************************** + * 8010.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS + * line endings + * December 8, 1999 Jon Taylor Added lots of new register info + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * + ********************************************************************** + */ + + +#ifndef _8010_H +#define _8010_H + +/* ------------------- DEFINES -------------------- */ + +#define EMUPAGESIZE 4096 /* don't change */ +#define RESERVED 0 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 /* don't change */ +#define MAXPAGES (32768 * NUM_G / EMUPAGESIZE) /* WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE */ + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) + +/************************************************************************************************/ +/* PCI function 0 registers, address = <val> + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define 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 DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ +#define IPR_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. */ + +#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_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 0x0x0000e0 /* 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_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 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#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. */ + +#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 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 = <val> + 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 */ + + +/********************************************************************************************************/ +/* AC97 pointer-offset register set, accessed through the AC97ADDRESS and AC97DATA registers */ +/********************************************************************************************************/ + +#define AC97_RESET 0x00 +#define AC97_MASTERVOLUME 0x02 /* Master volume */ +#define AC97_HEADPHONEVOLUME 0x04 /* Headphone volume */ +#define AC97_MASTERVOLUMEMONO 0x06 /* Mast volume mono */ +#define AC97_MASTERTONE 0x08 +#define AC97_PCBEEPVOLUME 0x0a /* PC speaker system beep volume */ +#define AC97_PHONEVOLUME 0x0c +#define AC97_MICVOLUME 0x0e +#define AC97_LINEINVOLUME 0x10 +#define AC97_CDVOLUME 0x12 +#define AC97_VIDEOVOLUME 0x14 +#define AC97_AUXVOLUME 0x16 +#define AC97_PCMOUTVOLUME 0x18 +#define AC97_RECORDSELECT 0x1a +#define AC97_RECORDGAIN 0x1c +#define AC97_RECORDGAINMIC 0x1e +#define AC97_GENERALPUPOSE 0x20 +#define AC97_3DCONTROL 0x22 +#define AC97_MODEMRATE 0x24 +#define AC97_POWERDOWN 0x26 +#define AC97_VENDORID1 0x7c +#define AC97_VENDORID2 0x7e +#define AC97_ZVIDEOVOLUME 0xec +#define AC97_AC3VOLUME 0xed + +/********************************************************************************************************/ +/* 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_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ + +#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 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_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 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 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 FXWC 0x43 /* FX output write channels register */ + /* When set, each bit enables the writing of the */ + /* corresponding FX output channel into host memory */ + +#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 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 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. */ + +#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 FXIDX 0x65 /* FX recording buffer index register */ +#define FXIDX_MASK 0x0000ffff /* 16-bit value */ +#define FXIDX_IDX 0x10000065 + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ + +/* 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 */ + +#endif /* _8010_H */ diff --git a/drivers/sound/emu10k1/Makefile b/drivers/sound/emu10k1/Makefile new file mode 100644 index 000000000..3f4d227f1 --- /dev/null +++ b/drivers/sound/emu10k1/Makefile @@ -0,0 +1,29 @@ +# Makefile for Creative Labs EMU10K1 +# +# 12 Apr 2000 Rui Sousa + +ifeq ($(CONFIG_SOUND_EMU10K1),y) + O_TARGET := emu10k1.o + O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o efxmgr.o \ + emuadxmg.o hwaccess.o irqmgr.o main.o midi.o mixer.o \ + osutils.o recmgr.o timer.o voicemgr.o +else + ifeq ($(CONFIG_SOUND_EMU10K1),m) + M_OBJS := emu10k1.o + O_TARGET := emu10k1.o + O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o efxmgr.o \ + emuadxmg.o hwaccess.o irqmgr.o main.o midi.o mixer.o \ + osutils.o recmgr.o timer.o voicemgr.o + endif +endif + +EXTRA_CFLAGS += -I. + +ifdef DEBUG + EXTRA_CFLAGS += -DEMU10K1_DEBUG +endif + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff --git a/drivers/sound/emu10k1/audio.c b/drivers/sound/emu10k1/audio.c new file mode 100644 index 000000000..a68287984 --- /dev/null +++ b/drivers/sound/emu10k1/audio.c @@ -0,0 +1,1441 @@ + +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardwo.h" +#include "cardwi.h" +#include "recmgr.h" +#include "audio.h" + +static void calculate_ofrag(struct woinst *); +static void calculate_ifrag(struct wiinst *); + +/* Audio file operations */ +static loff_t emu10k1_audio_llseek(struct file *file, loff_t offset, int nOrigin) +{ + return -ESPIPE; +} + +static ssize_t emu10k1_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in; + ssize_t ret = 0; + unsigned long flags; + + DPD(4, "emu10k1_audio_read(), buffer=%p, count=%x\n", buffer, (u32) count); + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->mapped) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -ENXIO; + } + + if (!wiinst->wave_in) { + calculate_ifrag(wiinst); + + while (emu10k1_wavein_open(wave_dev) != CTSTATUS_SUCCESS) { + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + UP_INODE_SEM(&inode->i_sem); + interruptible_sleep_on(&wave_dev->card->open_wait); + DOWN_INODE_SEM(&inode->i_sem); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&wiinst->lock, flags); + } + } + + wave_in = wiinst->wave_in; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + while (count > 0) { + u32 bytestocopy, dummy; + + spin_lock_irqsave(&wiinst->lock, flags); + + if ((wave_in->state != CARDWAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + emu10k1_wavein_start(wave_dev); + + emu10k1_wavein_getxfersize(wave_in, &bytestocopy, &dummy); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(4, "bytestocopy --> %x\n", bytestocopy); + + if ((bytestocopy >= wiinst->fragment_size) + || (bytestocopy >= count)) { + bytestocopy = min(bytestocopy, count); + + emu10k1_wavein_xferdata(wiinst, (u8 *) buffer, &bytestocopy); + + count -= bytestocopy; + buffer += bytestocopy; + ret += bytestocopy; + } + + if (count > 0) { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) + return (ret ? ret : -EAGAIN); + + UP_INODE_SEM(&inode->i_sem); + interruptible_sleep_on(&wiinst->wait_queue); + DOWN_INODE_SEM(&inode->i_sem); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + + } + } + + DPD(4, "bytes copied -> %x\n", (u32) ret); + + return ret; +} + +static ssize_t emu10k1_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out; + ssize_t ret; + unsigned long flags; + + GET_INODE_STRUCT(); + + DPD(4, "emu10k1_audio_write(), buffer=%p, count=%x\n", buffer, (u32) count); + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->mapped) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -ENXIO; + } + + if (!woinst->wave_out) { + calculate_ofrag(woinst); + + while (emu10k1_waveout_open(wave_dev) != CTSTATUS_SUCCESS) { + spin_unlock_irqrestore(&woinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + UP_INODE_SEM(&inode->i_sem); + interruptible_sleep_on(&wave_dev->card->open_wait); + DOWN_INODE_SEM(&inode->i_sem); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&woinst->lock, flags); + } + } + + wave_out = woinst->wave_out; + + spin_unlock_irqrestore(&woinst->lock, flags); + + ret = 0; + while (count > 0) { + u32 bytestocopy, pending, dummy; + + spin_lock_irqsave(&woinst->lock, flags); + + emu10k1_waveout_getxfersize(wave_out, &bytestocopy, &pending, &dummy); + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(4, "bytestocopy --> %x\n", bytestocopy); + + if ((bytestocopy >= woinst->fragment_size) + || (bytestocopy >= count)) { + + bytestocopy = min(bytestocopy, count); + + emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy); + + count -= bytestocopy; + buffer += bytestocopy; + ret += bytestocopy; + + spin_lock_irqsave(&woinst->lock, flags); + woinst->total_copied += bytestocopy; + + if ((wave_out->state != CARDWAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (woinst->total_copied >= woinst->fragment_size)) { + + if (emu10k1_waveout_start(wave_dev) != CTSTATUS_SUCCESS) { + spin_unlock_irqrestore(&woinst->lock, flags); + ERROR(); + return -EFAULT; + } + } + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (count > 0) { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) + return (ret ? ret : -EAGAIN); + + UP_INODE_SEM(&inode->i_sem); + interruptible_sleep_on(&woinst->wait_queue); + DOWN_INODE_SEM(&inode->i_sem); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + } + } + + DPD(4, "bytes copied -> %x\n", (u32) ret); + + return ret; +} + +static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + int val = 0; + struct woinst *woinst = NULL; + struct wave_out *wave_out = NULL; + struct wiinst *wiinst = NULL; + struct wave_in *wave_in = NULL; + u32 pending, bytestocopy, dummy; + unsigned long flags; + + DPF(4, "emu10k1_audio_ioctl()\n"); + + if (file->f_mode & FMODE_WRITE) { + woinst = wave_dev->woinst; + spin_lock_irqsave(&woinst->lock, flags); + wave_out = woinst->wave_out; + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + wiinst = wave_dev->wiinst; + spin_lock_irqsave(&wiinst->lock, flags); + wave_in = wiinst->wave_in; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + switch (cmd) { + case OSS_GETVERSION: + DPF(2, "OSS_GETVERSION:\n"); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_RESET: + DPF(2, "SNDCTL_DSP_RESET:\n"); + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + emu10k1_waveout_close(wave_dev); + + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + woinst->curpos = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + emu10k1_wavein_close(wave_dev); + + wiinst->total_recorded = 0; + wiinst->blocks = 0; + wiinst->curpos = 0; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SYNC: + DPF(2, "SNDCTL_DSP_SYNC:\n"); + + if (file->f_mode & FMODE_WRITE) { + + if (wave_out) { + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out->state == CARDWAVE_STATE_STARTED) + while ((woinst->total_played < woinst->total_copied) + && !signal_pending(current)) { + spin_unlock_irqrestore(&woinst->lock, flags); + interruptible_sleep_on(&woinst->wait_queue); + spin_lock_irqsave(&woinst->lock, flags); + } + + emu10k1_waveout_close(wave_dev); + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + woinst->curpos = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + emu10k1_wavein_close(wave_dev); + + wiinst->total_recorded = 0; + wiinst->blocks = 0; + wiinst->curpos = 0; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SETDUPLEX: + DPF(2, "SNDCTL_DSP_SETDUPLEX:\n"); + break; + + case SNDCTL_DSP_GETCAPS: + DPF(2, "SNDCTL_DSP_GETCAPS:\n"); + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC, (int *) arg); + + case SNDCTL_DSP_SPEED: + DPF(2, "SNDCTL_DSP_SPEED:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, "val is %d\n", val); + + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + woinst->wave_fmt.samplingrate = val; + + if (emu10k1_waveout_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = woinst->wave_fmt.samplingrate; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback sampling rate -> %d\n", val); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + wiinst->wave_fmt.samplingrate = val; + + if (emu10k1_wavein_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = wiinst->wave_fmt.samplingrate; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(2, "set recording sampling rate -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.samplingrate; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_STEREO: + DPF(2, "SNDCTL_DSP_STEREO:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + woinst->wave_fmt.channels = val ? 2 : 1; + + if (emu10k1_waveout_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = woinst->wave_fmt.channels - 1; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback stereo -> %d\n", val); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + wiinst->wave_fmt.channels = val ? 2 : 1; + + if (emu10k1_wavein_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = wiinst->wave_fmt.channels - 1; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording stereo -> %d\n", val); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_CHANNELS: + DPF(2, "SNDCTL_DSP_CHANNELS:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + woinst->wave_fmt.channels = val; + + if (emu10k1_waveout_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = woinst->wave_fmt.channels; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback number of channels -> %d\n", val); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + wiinst->wave_fmt.channels = val; + + if (emu10k1_wavein_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = wiinst->wave_fmt.channels; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording number of channels -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.channels; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_GETFMTS: + DPF(2, "SNDCTL_DSP_GETFMTS:\n"); + + if (file->f_mode & FMODE_READ) + val = AFMT_S16_LE; + else if (file->f_mode & FMODE_WRITE) + val = AFMT_S16_LE | AFMT_U8; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Same as SNDCTL_DSP_SAMPLESIZE */ + DPF(2, "SNDCTL_DSP_SETFMT:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + woinst->wave_fmt.bitsperchannel = val; + + if (emu10k1_waveout_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = woinst->wave_fmt.bitsperchannel; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback sample size -> %d\n", val); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + wiinst->wave_fmt.bitsperchannel = val; + + if (emu10k1_wavein_setformat(wave_dev) != CTSTATUS_SUCCESS) + return -EINVAL; + + val = wiinst->wave_fmt.bitsperchannel; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording sample size -> %d\n", val); + } + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.bitsperchannel; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.bitsperchannel; + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg); + } + break; + + case SOUND_PCM_READ_BITS: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.bitsperchannel; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.bitsperchannel; + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg); + + case SOUND_PCM_READ_RATE: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.samplingrate; + + return put_user(val, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.channels; + + return put_user(val, (int *) arg); + + case SOUND_PCM_WRITE_FILTER: + DPF(2, "SOUND_PCM_WRITE_FILTER: not implemented\n"); + break; + + case SOUND_PCM_READ_FILTER: + DPF(2, "SOUND_PCM_READ_FILTER: not implemented\n"); + break; + + case SNDCTL_DSP_SETSYNCRO: + DPF(2, "SNDCTL_DSP_SETSYNCRO: not implemented\n"); + break; + + case SNDCTL_DSP_GETTRIGGER: + DPF(2, "SNDCTL_DSP_GETTRIGGER:\n"); + + if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)) + val |= PCM_ENABLE_OUTPUT; + if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + val |= PCM_ENABLE_INPUT; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + DPF(2, "SNDCTL_DSP_SETTRIGGER:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (val & PCM_ENABLE_OUTPUT) { + wave_dev->enablebits |= PCM_ENABLE_OUTPUT; + if (wave_out) + emu10k1_waveout_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT; + if (wave_out) + emu10k1_waveout_stop(wave_dev); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (val & PCM_ENABLE_INPUT) { + wave_dev->enablebits |= PCM_ENABLE_INPUT; + if (wave_in) + emu10k1_wavein_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_INPUT; + if (wave_in) + emu10k1_wavein_stop(wave_dev); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + break; + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETOSPACE:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + if (wave_out) { + spin_lock_irqsave(&woinst->lock, flags); + emu10k1_waveout_getxfersize(wave_out, &bytestocopy, &pending, &dummy); + spin_unlock_irqrestore(&woinst->lock, flags); + + info.bytes = bytestocopy; + } else { + spin_lock_irqsave(&woinst->lock, flags); + calculate_ofrag(woinst); + spin_unlock_irqrestore(&woinst->lock, flags); + + info.bytes = woinst->numfrags * woinst->fragment_size; + } + + info.fragstotal = woinst->numfrags; + info.fragments = info.bytes / woinst->fragment_size; + info.fragsize = woinst->fragment_size; + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETISPACE:\n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + if (wave_in) { + spin_lock_irqsave(&wiinst->lock, flags); + emu10k1_wavein_getxfersize(wave_in, &bytestocopy, &dummy); + spin_unlock_irqrestore(&wiinst->lock, flags); + + info.bytes = bytestocopy; + } else { + spin_lock_irqsave(&wiinst->lock, flags); + calculate_ifrag(wiinst); + spin_unlock_irqrestore(&wiinst->lock, flags); + + info.bytes = 0; + } + + info.fragstotal = wiinst->numfrags; + info.fragments = info.bytes / wiinst->fragment_size; + info.fragsize = wiinst->fragment_size; + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_NONBLOCK: + DPF(2, "SNDCTL_DSP_NONBLOCK:\n"); + + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_GETODELAY: + DPF(4, "SNDCTL_DSP_GETODELAY:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + if (wave_out) { + spin_lock_irqsave(&woinst->lock, flags); + emu10k1_waveout_getxfersize(wave_out, &bytestocopy, &pending, &dummy); + spin_unlock_irqrestore(&woinst->lock, flags); + + val = pending; + } else + val = 0; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETIPTR: \n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) { + emu10k1_wavein_getcontrol(wave_in, WAVECURPOS, (u32 *) & cinfo.ptr); + cinfo.bytes = + cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % (wiinst->fragment_size * wiinst->numfrags); + cinfo.blocks = cinfo.bytes / wiinst->fragment_size - wiinst->blocks; + wiinst->blocks = cinfo.bytes / wiinst->fragment_size; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + wiinst->blocks = 0; + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETOPTR:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) { + emu10k1_waveout_getcontrol(wave_out, WAVECURPOS, (u32 *) & cinfo.ptr); + cinfo.bytes = cinfo.ptr + woinst->total_played - woinst->total_played % (woinst->fragment_size * woinst->numfrags); + cinfo.blocks = cinfo.bytes / woinst->fragment_size - woinst->blocks; + woinst->blocks = cinfo.bytes / woinst->fragment_size; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + woinst->blocks = 0; + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETBLKSIZE: + DPF(2, "SNDCTL_DSP_GETBLKSIZE:\n"); + + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + calculate_ofrag(woinst); + val = woinst->fragment_size; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + calculate_ifrag(wiinst); + val = wiinst->fragment_size; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_POST: + DPF(2, "SNDCTL_DSP_POST: not implemented\n"); + break; + + case SNDCTL_DSP_SUBDIVIDE: + DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n"); + break; + + case SNDCTL_DSP_SETFRAGMENT: + DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + + DPD(2, "val is %x\n", val); + + if (val == 0) + return -EIO; + + if (file->f_mode & FMODE_WRITE) { + if (wave_out) + return -EINVAL; /* too late to change */ + + woinst->ossfragshift = val & 0xffff; + woinst->numfrags = (val >> 16) & 0xffff; + } + + if (file->f_mode & FMODE_READ) { + if (wave_in) + return -EINVAL; /* too late to change */ + + wiinst->ossfragshift = val & 0xffff; + wiinst->numfrags = (val >> 16) & 0xffff; + } + + break; + + case SNDCTL_COPR_LOAD: + { + copr_buffer buf; + u32 i; + + DPF(2, "SNDCTL_COPR_LOAD:\n"); + + if (copy_from_user(&buf, (copr_buffer *) arg, sizeof(buf))) + return -EFAULT; + + if ((buf.command != 1) && (buf.command != 2)) + return -EINVAL; + + if (((buf.offs < 0x100) && (buf.command == 2)) + || (buf.offs < 0x000) + || (buf.offs + buf.len > 0x800) || (buf.len > 1000)) + return -EINVAL; + + if (buf.command == 1) { + for (i = 0; i < buf.len; i++) + + ((u32 *) buf.data)[i] = sblive_readptr(wave_dev->card, buf.offs + i, 0); + if (copy_to_user((copr_buffer *) arg, &buf, sizeof(buf))) + return -EFAULT; + } else { + for (i = 0; i < buf.len; i++) + sblive_writeptr(wave_dev->card, buf.offs + i, 0, ((u32 *) buf.data)[i]); + } + break; + } + + default: /* Default is unrecognized command */ + DPD(2, "default: %x\n", cmd); + return -EINVAL; + } + return 0; +} + +static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + + DPF(2, "emu10k1_audio_mmap()\n"); + + if (vma_get_pgoff(vma) != 0) + return -ENXIO; + + if (vma->vm_flags & VM_WRITE) { + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out; + u32 size; + unsigned long flags; + int i; + + spin_lock_irqsave(&woinst->lock, flags); + + wave_out = woinst->wave_out; + + if (!wave_out) { + calculate_ofrag(woinst); + + if (emu10k1_waveout_open(wave_dev) != CTSTATUS_SUCCESS) { + spin_unlock_irqrestore(&woinst->lock, flags); + ERROR(); + return -EINVAL; + } + + wave_out = woinst->wave_out; + + /* Now mark the pages as reserved, otherwise remap_page_range doesn't do what we want */ + for (i = 0; i < wave_out->wavexferbuf->numpages; i++) + set_bit(PG_reserved, &mem_map[MAP_NR(wave_out->pagetable[i])].flags); + } + + size = vma->vm_end - vma->vm_start; + + if (size > (PAGE_SIZE * wave_out->wavexferbuf->numpages)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + for (i = 0; i < wave_out->wavexferbuf->numpages; i++) { + if (remap_page_range(vma->vm_start + (i * PAGE_SIZE), virt_to_phys(wave_out->pagetable[i]), PAGE_SIZE, vma->vm_page_prot)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EAGAIN; + } + } + + woinst->mapped = 1; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (vma->vm_flags & VM_READ) { + struct wiinst *wiinst = wave_dev->wiinst; + unsigned long flags; + + spin_lock_irqsave(&wiinst->lock, flags); + wiinst->mapped = 1; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return 0; +} + +static int emu10k1_audio_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct emu10k1_card *card; + struct list_head *entry; + struct emu10k1_wavedevice *wave_dev; + + DPF(2, "emu10k1_audio_open()\n"); + + /* Check for correct device to open */ + + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (card->audio1_num == minor || card->audio2_num == minor) + break; + } + + if (entry == &emu10k1_devs) + return -ENODEV; + + MOD_INC_USE_COUNT; + + if ((wave_dev = (struct emu10k1_wavedevice *) + kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL)) == NULL) { + ERROR(); + MOD_DEC_USE_COUNT; + return -EINVAL; + } + + wave_dev->card = card; + wave_dev->wiinst = NULL; + wave_dev->woinst = NULL; + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; /* Default */ + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst; + + if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) { + ERROR(); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + woinst->wave_fmt.samplingrate = 8000; + woinst->wave_fmt.bitsperchannel = 8; + woinst->wave_fmt.channels = 1; + woinst->ossfragshift = 0; + woinst->fragment_size = 0; + woinst->numfrags = 0; + woinst->device = (card->audio2_num == minor); + woinst->wave_out = NULL; + + init_waitqueue_head(&woinst->wait_queue); + + woinst->mapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + woinst->curpos = 0; + woinst->lock = SPIN_LOCK_UNLOCKED; + wave_dev->woinst = woinst; + +#ifdef PRIVATE_PCM_VOLUME + { + int i; + int j = -1; + + /* + * find out if we've already been in this table + * xmms reopens dsp on every move of slider + * this way we keep the same local pcm for such + * process + */ + for (i = 0; i < MAX_PCM_CHANNELS; i++) { + if (sblive_pcm_volume[i].files == current->files) + break; + // here we should select last used memeber + // improve me in case its not sufficient + if (j < 0 && !sblive_pcm_volume[i].opened) + j = i; + } + // current task not found + if (i == MAX_PCM_CHANNELS) { + // add new entry + if (j < 0) + printk("TOO MANY WRITTERS!!!\n"); + i = (j >= 0) ? j : 0; + DPD(2, "new pcm private %p\n", current->files); + sblive_pcm_volume[i].files = current->files; + sblive_pcm_volume[i].mixer = 0x6464; // max + sblive_pcm_volume[i].attn_l = 0; + sblive_pcm_volume[i].attn_r = 0; + sblive_pcm_volume[i].channel_l = NUM_G; + sblive_pcm_volume[i].channel_r = NUM_G; + } + sblive_pcm_volume[i].opened++; + } +#endif + } + + if (file->f_mode & FMODE_READ) { + /* Recording */ + struct wiinst *wiinst; + + if ((wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL)) == NULL) { + ERROR(); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + switch (card->wavein->recsrc) { + case WAVERECORD_AC97: + wiinst->wave_fmt.samplingrate = 8000; + wiinst->wave_fmt.bitsperchannel = 8; + wiinst->wave_fmt.channels = 1; + break; + case WAVERECORD_MIC: + wiinst->wave_fmt.samplingrate = 8000; + wiinst->wave_fmt.bitsperchannel = 8; + wiinst->wave_fmt.channels = 1; + break; + case WAVERECORD_FX: + wiinst->wave_fmt.samplingrate = 48000; + wiinst->wave_fmt.bitsperchannel = 16; + wiinst->wave_fmt.channels = 2; + break; + default: + break; + } + + wiinst->recsrc = card->wavein->recsrc; + wiinst->ossfragshift = 0; + wiinst->fragment_size = 0; + wiinst->numfrags = 0; + wiinst->wave_in = NULL; + + init_waitqueue_head(&wiinst->wait_queue); + + wiinst->mapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + wiinst->curpos = 0; + wiinst->lock = SPIN_LOCK_UNLOCKED; + wave_dev->wiinst = wiinst; + } + + file->private_data = (void *) wave_dev; + + return 0; /* Success? */ +} + +static int emu10k1_audio_release(struct inode *inode, struct file *file) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct emu10k1_card *card = wave_dev->card; + unsigned long flags; + + DPF(2, "emu10k1_audio_release()\n"); + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out; + + spin_lock_irqsave(&woinst->lock, flags); + + wave_out = woinst->wave_out; + + if (wave_out) { + if ((wave_out->state == CARDWAVE_STATE_STARTED) + && !(file->f_flags & O_NONBLOCK)) { + while (!signal_pending(current) + && (woinst->total_played < woinst->total_copied)) { + DPF(4, "Buffer hasn't been totally played, sleep....\n"); + spin_unlock_irqrestore(&woinst->lock, flags); + interruptible_sleep_on(&woinst->wait_queue); + spin_lock_irqsave(&woinst->lock, flags); + } + } + + if (woinst->mapped && wave_out->pagetable) { + int i; + + /* Undo marking the pages as reserved */ + for (i = 0; i < woinst->wave_out->wavexferbuf->numpages; i++) + set_bit(PG_reserved, &mem_map[MAP_NR(woinst->wave_out->pagetable[i])].flags); + } + + woinst->mapped = 0; + emu10k1_waveout_close(wave_dev); + } +#ifdef PRIVATE_PCM_VOLUME + { + int i; + + /* mark as closed + * NOTE: structure remains unchanged for next reopen */ + for (i = 0; i < MAX_PCM_CHANNELS; i++) { + if (sblive_pcm_volume[i].files == current->files) { + sblive_pcm_volume[i].opened--; + break; + } + } + } +#endif + spin_unlock_irqrestore(&woinst->lock, flags); + kfree(wave_dev->woinst); + } + + if (file->f_mode & FMODE_READ) { + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in; + + spin_lock_irqsave(&wiinst->lock, flags); + + wave_in = wiinst->wave_in; + + if (wave_in) { + wiinst->mapped = 0; + emu10k1_wavein_close(wave_dev); + } + spin_unlock_irqrestore(&wiinst->lock, flags); + kfree(wave_dev->wiinst); + } + + kfree(wave_dev); + + wake_up_interruptible(&card->open_wait); + MOD_DEC_USE_COUNT; + + return 0; +} + +static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct woinst *woinst = wave_dev->woinst; + struct wiinst *wiinst = wave_dev->wiinst; + unsigned int mask = 0; + u32 bytestocopy, pending, dummy; + unsigned long flags; + + DPF(4, "emu10k1_audio_poll()\n"); + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &woinst->wait_queue, wait); + + if (file->f_mode & FMODE_READ) + poll_wait(file, &wiinst->wait_queue, wait); + + if (file->f_mode & FMODE_WRITE) { + struct wave_out *wave_out; + + spin_lock_irqsave(&woinst->lock, flags); + + wave_out = woinst->wave_out; + + if (wave_out) { + + emu10k1_waveout_getxfersize(wave_out, &bytestocopy, &pending, &dummy); + + if (bytestocopy >= woinst->fragment_size) + mask |= POLLOUT | POLLWRNORM; + } else + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + struct wave_in *wave_in; + + spin_lock_irqsave(&wiinst->lock, flags); + + wave_in = wiinst->wave_in; + + if (!wave_in) { + calculate_ifrag(wiinst); + if (emu10k1_wavein_open(wave_dev) != CTSTATUS_SUCCESS) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return (mask |= POLLERR); + } + + wave_in = wiinst->wave_in; + } + + if (wave_in->state != CARDWAVE_STATE_STARTED) { + wave_dev->enablebits |= PCM_ENABLE_INPUT; + emu10k1_wavein_start(wave_dev); + } + + emu10k1_wavein_getxfersize(wave_in, &bytestocopy, &dummy); + + if (bytestocopy >= wiinst->fragment_size) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return mask; +} + +static void calculate_ofrag(struct woinst *woinst) +{ + u32 fragsize, bytespersec; + + if (woinst->fragment_size) + return; + + bytespersec = woinst->wave_fmt.channels * (woinst->wave_fmt.bitsperchannel >> 3) * woinst->wave_fmt.samplingrate; + + if (!woinst->ossfragshift) { + fragsize = (bytespersec * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + woinst->ossfragshift++; + } + } + + if (woinst->ossfragshift < WAVEOUT_MINFRAGSHIFT) + woinst->ossfragshift = WAVEOUT_MINFRAGSHIFT; + + woinst->fragment_size = 1 << woinst->ossfragshift; + + if (!woinst->numfrags) { + u32 numfrags; + + numfrags = (bytespersec * WAVEOUT_DEFAULTBUFLEN) / (woinst->fragment_size * 1000) - 1; + + woinst->numfrags = 1; + + while (numfrags) { + numfrags >>= 1; + woinst->numfrags <<= 1; + } + } + + if (woinst->numfrags < MINFRAGS) + woinst->numfrags = MINFRAGS; + + if (woinst->numfrags * woinst->fragment_size > WAVEOUT_MAXBUFSIZE) { + woinst->numfrags = WAVEOUT_MAXBUFSIZE / woinst->fragment_size; + + if (woinst->numfrags < MINFRAGS) { + woinst->numfrags = MINFRAGS; + woinst->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS; + } + + } else if (woinst->numfrags * woinst->fragment_size < WAVEOUT_MINBUFSIZE) + woinst->numfrags = WAVEOUT_MINBUFSIZE / woinst->fragment_size; + + DPD(2, " calculated playback fragment_size -> %d\n", woinst->fragment_size); + DPD(2, " calculated playback numfrags -> %d\n", woinst->numfrags); +} + +static void calculate_ifrag(struct wiinst *wiinst) +{ + u32 fragsize, bytespersec; + + if (wiinst->fragment_size) + return; + + bytespersec = wiinst->wave_fmt.channels * (wiinst->wave_fmt.bitsperchannel >> 3) * wiinst->wave_fmt.samplingrate; + + if (!wiinst->ossfragshift) { + fragsize = (bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + wiinst->ossfragshift++; + } + } + + if (wiinst->ossfragshift < WAVEIN_MINFRAGSHIFT) + wiinst->ossfragshift = WAVEIN_MINFRAGSHIFT; + + wiinst->fragment_size = 1 << wiinst->ossfragshift; + + if (!wiinst->numfrags) + wiinst->numfrags = (bytespersec * WAVEIN_DEFAULTBUFLEN) / (wiinst->fragment_size * 1000) - 1; + + if (wiinst->numfrags < MINFRAGS) + wiinst->numfrags = MINFRAGS; + + if (wiinst->numfrags * wiinst->fragment_size > WAVEIN_MAXBUFSIZE) { + wiinst->numfrags = WAVEIN_MAXBUFSIZE / wiinst->fragment_size; + + if (wiinst->numfrags < MINFRAGS) { + wiinst->numfrags = MINFRAGS; + wiinst->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS; + } + } else if (wiinst->numfrags * wiinst->fragment_size < WAVEIN_MINBUFSIZE) + wiinst->numfrags = WAVEIN_MINBUFSIZE / wiinst->fragment_size; + + DPD(2, " calculated recording fragment_size -> %d\n", wiinst->fragment_size); + DPD(2, " calculated recording numfrags -> %d\n", wiinst->numfrags); +} + +void emu10k1_wavein_bh(unsigned long refdata) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in = wiinst->wave_in; + u32 bytestocopy, curpos; + unsigned long flags; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in->state == CARDWAVE_STATE_STOPPED) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return; + } + + emu10k1_wavein_getxfersize(wave_in, &bytestocopy, &curpos); + + wiinst->total_recorded += curpos - wiinst->curpos; + + if (curpos < wiinst->curpos) + wiinst->total_recorded += wiinst->fragment_size * wiinst->numfrags; + + wiinst->curpos = curpos; + + if (wiinst->mapped) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return; + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (bytestocopy >= wiinst->fragment_size) + wake_up_interruptible(&wiinst->wait_queue); + else + DPD(4, "Not enough transfer size, %d\n", bytestocopy); + + return; +} + +void emu10k1_waveout_bh(unsigned long refdata) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + u32 bytestocopy, pending, curpos; + unsigned long flags; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out->state == CARDWAVE_STATE_STOPPED) { + spin_unlock_irqrestore(&woinst->lock, flags); + return; + } + + emu10k1_waveout_getxfersize(wave_out, &bytestocopy, &pending, &curpos); + + woinst->total_played += curpos - woinst->curpos; + + if (curpos < woinst->curpos) + woinst->total_played += woinst->fragment_size * woinst->numfrags; + + woinst->curpos = curpos; + + if (woinst->mapped) { + spin_unlock_irqrestore(&woinst->lock, flags); + return; + } + + if (wave_out->fill_silence) { + spin_unlock_irqrestore(&woinst->lock, flags); + emu10k1_waveout_fillsilence(woinst); + } else + spin_unlock_irqrestore(&woinst->lock, flags); + + if (bytestocopy >= woinst->fragment_size) + wake_up_interruptible(&woinst->wait_queue); + else + DPD(4, "Not enough transfer size -> %x\n", bytestocopy); + + return; +} + +struct file_operations emu10k1_audio_fops = { + llseek:emu10k1_audio_llseek, + read:emu10k1_audio_read, + write:emu10k1_audio_write, + poll:emu10k1_audio_poll, + ioctl:emu10k1_audio_ioctl, + mmap:emu10k1_audio_mmap, + open:emu10k1_audio_open, + release:emu10k1_audio_release, +}; diff --git a/drivers/sound/emu10k1/audio.h b/drivers/sound/emu10k1/audio.h new file mode 100644 index 000000000..357479acb --- /dev/null +++ b/drivers/sound/emu10k1/audio.h @@ -0,0 +1,46 @@ +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _AUDIO_H +#define _AUDIO_H + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/poll.h> +#include <asm/uaccess.h> + +#define MINFRAGS 2 /* _don't_ got bellow 2 */ + +void emu10k1_waveout_bh(unsigned long); +void emu10k1_wavein_bh(unsigned long); + +#endif /* _AUDIO_H */ diff --git a/drivers/sound/emu10k1/cardmi.c b/drivers/sound/emu10k1/cardmi.c new file mode 100644 index 000000000..f741b9d6d --- /dev/null +++ b/drivers/sound/emu10k1/cardmi.c @@ -0,0 +1,813 @@ + +/* + ********************************************************************** + * sblive_mi.c - MIDI UART input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox clean up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmi.h" + +static struct { + int (*Fn) (struct emu10k1_mpuin *, u8); +} midistatefn[] = { + + { + sblive_miStateParse}, { + sblive_miState3Byte}, /* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */ + { + sblive_miState3ByteKey}, /* Byte 1 */ + { + sblive_miState3ByteVel}, /* Byte 2 */ + { + sblive_miState2Byte}, /* 0xCn, 0xDn */ + { + sblive_miState2ByteKey}, /* Byte 1 */ + { + sblive_miStateSysCommon2}, /* 0xF1 , 0xF3 */ + { + sblive_miStateSysCommon2Key}, /* 0xF1 , 0xF3, Byte 1 */ + { + sblive_miStateSysCommon3}, /* 0xF2 */ + { + sblive_miStateSysCommon3Key}, /* 0xF2 , Byte 1 */ + { + sblive_miStateSysCommon3Vel}, /* 0xF2 , Byte 2 */ + { + sblive_miStateSysExNorm}, /* 0xF0, 0xF7, Normal mode */ + { + sblive_miStateSysReal} /* 0xF4 - 0xF6 ,0xF8 - 0xFF */ +}; + +/* Installs the IRQ handler for the MPU in port */ + +/* and initialize parameters */ + +int emu10k1_mpuin_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + DPF(2, "emu10k1_mpuin_open\n"); + + if (!(card_mpuin->status & FLAGS_AVAILABLE)) + return CTSTATUS_INUSE; + + /* Copy open info and mark channel as in use */ + card_mpuin->openinfo = *openinfo; + card_mpuin->status &= ~FLAGS_AVAILABLE; /* clear */ + card_mpuin->status |= FLAGS_READY; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + card_mpuin->firstmidiq = NULL; + card_mpuin->lastmidiq = NULL; + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + sblive_miStateInit(card_mpuin); + + emu10k1_mpu_reset(card); + emu10k1_mpu_acquire(card); + + return CTSTATUS_SUCCESS; +} + +int emu10k1_mpuin_close(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + DPF(2, "emu10k1_mpuin_close()\n"); + + /* Check if there are pending input SysEx buffers */ + if (card_mpuin->firstmidiq != NULL) { + ERROR(); + return CTSTATUS_ERROR; + } + + /* Disable RX interrupt */ + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + emu10k1_mpu_release(card); + + card_mpuin->status |= FLAGS_AVAILABLE; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + return CTSTATUS_SUCCESS; +} + +/* Adds MIDI buffer to local queue list */ + +int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *card_mpuin, struct midi_hdr *midihdr) +{ + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuin_add_buffer()\n"); + + /* Update MIDI buffer flags */ + midihdr->flags |= MIDIBUF_INQUEUE; /* set */ + midihdr->flags &= ~MIDIBUF_DONE; /* clear */ + + if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) { + /* Message lost */ + return CTSTATUS_ERROR; + } + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->data; + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + if (card_mpuin->firstmidiq == NULL) { + card_mpuin->firstmidiq = midiq; + card_mpuin->lastmidiq = midiq; + } else { + (card_mpuin->lastmidiq)->next = midiq; + card_mpuin->lastmidiq = midiq; + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + return CTSTATUS_SUCCESS; +} + +/* First set the Time Stamp if MIDI IN has not started. */ + +/* Then enable RX Irq. */ + +int emu10k1_mpuin_start(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + u8 dummy; + + DPF(2, "emu10k1_mpuin_start()\n"); + + /* Set timestamp if not set */ + if (card_mpuin->status & FLAGS_MIDM_STARTED) { + DPF(2, "Time Stamp not changed\n"); + } else { + while (emu10k1_mpu_read_data(card, &dummy) == CTSTATUS_SUCCESS); + + card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */ + + /* Set new time stamp */ + card_mpuin->timestart = (jiffies * 1000) / HZ; + DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart); + + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + emu10k1_irq_enable(card, INTE_MIDIRXENABLE); + } + + return CTSTATUS_SUCCESS; +} + +/* Disable the RX Irq. If a partial recorded buffer */ + +/* exist, send it up to IMIDI level. */ + +int emu10k1_mpuin_stop(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuin_stop()\n"); + + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + if (card_mpuin->firstmidiq) { + spin_lock_irqsave(&card_mpuin->lock, flags); + + midiq = card_mpuin->firstmidiq; + if (midiq != NULL) { + if (midiq->sizeLeft == midiq->length) + midiq = NULL; + else { + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + } + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + if (midiq) { + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + kfree(midiq); + } + } + + return CTSTATUS_SUCCESS; +} + +/* Disable the RX Irq. If any buffer */ + +/* exist, send it up to IMIDI level. */ +int emu10k1_mpuin_reset(struct emu10k1_card *card) +{ + struct emu10k1_mpuin *card_mpuin = card->mpuin; + struct midi_queue *midiq; + + DPF(2, "emu10k1_mpuin_reset()\n"); + + emu10k1_irq_disable(card, INTE_MIDIRXENABLE); + + while (card_mpuin->firstmidiq) { + midiq = card_mpuin->firstmidiq; + card_mpuin->firstmidiq = midiq->next; + + if (midiq->sizeLeft == midiq->length) + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + else + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + + kfree(midiq); + } + + card_mpuin->lastmidiq = NULL; + card_mpuin->status &= ~FLAGS_MIDM_STARTED; + + return CTSTATUS_SUCCESS; +} + +/* Passes the message with the data back to the client */ + +/* via IRQ & DPC callbacks to Ring 3 */ +int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid) +{ + unsigned long timein; + struct midi_queue *midiq; + unsigned long callback_msg[3]; + struct midi_hdr *midihdr; + + /* Called during ISR. The data & code touched are: + * 1. card_mpuin + * 2. The function to be called + */ + + timein = card_mpuin->timein; + if (card_mpuin->timestart <= timein) + callback_msg[0] = timein - card_mpuin->timestart; + else + callback_msg[0] = (~0x0L - card_mpuin->timestart) + timein; + + if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) { + callback_msg[1] = data; + callback_msg[2] = bytesvalid; + DPD(2, "emu10k1_mpuin_callback: midimsg = %lx\n", data); + } else { + midiq = (struct midi_queue *) data; + midihdr = (struct midi_hdr *) midiq->refdata; + + callback_msg[1] = midiq->length - midiq->sizeLeft; + callback_msg[2] = midiq->refdata; + midihdr->flags &= ~MIDIBUF_INQUEUE; + midihdr->flags |= MIDIBUF_DONE; + + midihdr->bytesrecorded = midiq->length - midiq->sizeLeft; + } + + /* Notify client that Sysex buffer has been sent */ + emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg); + + return CTSTATUS_SUCCESS; +} + +void emu10k1_mpuin_bh(unsigned long refdata) +{ + u8 data; + unsigned idx; + struct emu10k1_mpuin *card_mpuin = (struct emu10k1_mpuin *) refdata; + unsigned long flags; + + while (card_mpuin->qhead != card_mpuin->qtail) { + spin_lock_irqsave(&card_mpuin->lock, flags); + idx = card_mpuin->qhead; + data = card_mpuin->midiq[idx].data; + card_mpuin->timein = card_mpuin->midiq[idx].timein; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + card_mpuin->qhead = idx; + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_miStateEntry(card_mpuin, data); + } + + return; +} + +/* IRQ callback handler routine for the MPU in port */ + +int emu10k1_mpuin_irqhandler(struct emu10k1_card *card) +{ + unsigned idx; + unsigned count; + u8 MPUIvalue; + struct emu10k1_mpuin *card_mpuin = card->mpuin; + + /* IRQ service routine. The data and code touched are: + * 1. card_mpuin + */ + + count = 0; + idx = card_mpuin->qtail; + + while (1) { + if (emu10k1_mpu_read_data(card, &MPUIvalue) == CTSTATUS_SUCCESS) { + ++count; + card_mpuin->midiq[idx].data = MPUIvalue; + card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + } else { + break; + } + } + + if (count) { + card_mpuin->qtail = idx; + + tasklet_hi_schedule(&card_mpuin->tasklet); + } + + return CTSTATUS_SUCCESS; +} + +/*****************************************************************************/ + +/* Supporting functions for Midi-In Interpretation State Machine */ + +/*****************************************************************************/ + +/* FIXME: This should be a macro */ +int sblive_miStateInit(struct emu10k1_mpuin *card_mpuin) +{ + card_mpuin->status = 0; /* For MIDI running status */ + card_mpuin->fstatus = 0; /* For 0xFn status only */ + card_mpuin->curstate = STIN_PARSE; + card_mpuin->laststate = STIN_PARSE; + card_mpuin->data = 0; + card_mpuin->timestart = 0; + card_mpuin->timein = 0; + + return CTSTATUS_SUCCESS; +} + +/* FIXME: This should be a macro */ +int sblive_miStateEntry(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + +int sblive_miStateParse(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + switch (data & 0xf0) { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + card_mpuin->curstate = STIN_3BYTE; + break; + + case 0xC0: + case 0xD0: + card_mpuin->curstate = STIN_2BYTE; + break; + + case 0xF0: + /* System messages do not affect the previous running status! */ + switch (data & 0x0f) { + case 0x0: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_EX_NORM; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + return CTSTATUS_NEXT_BYTE; + + case 0x7: + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0); + return CTSTATUS_ERROR; + + case 0x2: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_3; + break; + + case 0x1: + case 0x3: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_2; + break; + + default: + /* includes 0xF4 - 0xF6, 0xF8 - 0xFF */ + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + } + + break; + + default: + DPF(2, "BUG: default case hit\n"); + return CTSTATUS_ERROR; + } + + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + +int sblive_miState3Byte(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + u8 temp = data & 0xf0; + + if (temp < 0x80) { + return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data); + } else if (temp <= 0xe0 && temp != 0xc0 && temp != 0xd0) { + card_mpuin->status = data; + card_mpuin->curstate = STIN_3BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + +int sblive_miState3ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((unsigned long) data) << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_3BYTE_VEL; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miState3ByteVel(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 2 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((unsigned long) data) << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = STIN_3BYTE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return CTSTATUS_SUCCESS; +} + +int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + u8 temp = data & 0xf0; + + if ((temp == 0xc0) || (temp == 0xd0)) { + card_mpuin->status = data; + card_mpuin->curstate = STIN_2BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + if (temp < 0x80) + return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data); + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + +int sblive_miState2ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = STIN_2BYTE; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->status; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return CTSTATUS_SUCCESS; +} + +int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_2_KEY; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return CTSTATUS_SUCCESS; +} + +int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_KEY; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 1 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_VEL; + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *card_mpuin, u8 data) + +/* byte 2 */ +{ + unsigned long tmp; + + if (data > 0x7f) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (unsigned long) data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->data; + tmp = tmp << 8; + tmp |= (unsigned long) card_mpuin->fstatus; + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return CTSTATUS_SUCCESS; +} + +int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + unsigned long flags; + + if ((data > 0x7f) && (data != 0xf7)) { + /* Real-time messages check */ + if (data > 0xf7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid Data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0); + + kfree(midiq); + } + + return CTSTATUS_ERROR; + } + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + if (data == 0xf7) { + /* End of Sysex buffer */ + /* Send down the buffer */ + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + + kfree(midiq); + } + + return CTSTATUS_SUCCESS; + } + + if (card_mpuin->firstmidiq) { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + + if (midiq->sizeLeft == 0) { + /* Special case */ + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0); + + kfree(midiq); + + return CTSTATUS_NEXT_BYTE; + } + } + + return CTSTATUS_NEXT_BYTE; +} + +int sblive_miStateSysReal(struct emu10k1_mpuin *card_mpuin, u8 data) +{ + emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, data, 1); + + return CTSTATUS_NEXT_BYTE; +} diff --git a/drivers/sound/emu10k1/cardmi.h b/drivers/sound/emu10k1/cardmi.h new file mode 100644 index 000000000..f78fc1f70 --- /dev/null +++ b/drivers/sound/emu10k1/cardmi.h @@ -0,0 +1,113 @@ +/* + ********************************************************************** + * sblive_mi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMI_H +#define _CARDMI_H + +#include "icardmid.h" + +typedef enum +{ + STIN_PARSE = 0, + STIN_3BYTE, /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */ + STIN_3BYTE_KEY, /* Byte 1 */ + STIN_3BYTE_VEL, /* Byte 1 */ + STIN_2BYTE, /* 0xC0, 0xD0 */ + STIN_2BYTE_KEY, /* Byte 1 */ + STIN_SYS_COMMON_2, /* 0xF1, 0xF3 */ + STIN_SYS_COMMON_2_KEY, + STIN_SYS_COMMON_3, /* 0xF2 */ + STIN_SYS_COMMON_3_KEY, + STIN_SYS_COMMON_3_VEL, + STIN_SYS_EX_NORM, /* 0xF0, Normal mode */ + STIN_SYS_REAL +} midi_in_state; + + +/* flags for card MIDI in object */ +#define FLAGS_MIDM_STARTED 0x00001000 // Data has started to come in after Midm Start +#define MIDIIN_MAX_BUFFER_SIZE 200 // Definition for struct emu10k1_mpuin + +struct midi_data +{ + u8 data; + u32 timein; +}; + +struct emu10k1_mpuin +{ + spinlock_t lock; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + unsigned qhead, qtail; + struct midi_data midiq[MIDIIN_MAX_BUFFER_SIZE]; + struct tasklet_struct tasklet; + struct midi_openinfo openinfo; + + /* For MIDI state machine */ + u8 status; /* For MIDI running status */ + u8 fstatus; /* For 0xFn status only */ + midi_in_state curstate; + midi_in_state laststate; + u32 timestart; + u32 timein; + u8 data; +}; + +int emu10k1_mpuin_open(struct emu10k1_card *, struct midi_openinfo *); +int emu10k1_mpuin_close(struct emu10k1_card *); +int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *, struct midi_hdr *); +int emu10k1_mpuin_start(struct emu10k1_card *); +int emu10k1_mpuin_stop(struct emu10k1_card *); +int emu10k1_mpuin_reset(struct emu10k1_card *); + +int sblive_miStateInit(struct emu10k1_mpuin *); +int sblive_miStateEntry(struct emu10k1_mpuin *, u8); +int sblive_miStateParse(struct emu10k1_mpuin *, u8); +int sblive_miState3Byte(struct emu10k1_mpuin *, u8); +int sblive_miState3ByteKey(struct emu10k1_mpuin *, u8); +int sblive_miState3ByteVel(struct emu10k1_mpuin *, u8); +int sblive_miState2Byte(struct emu10k1_mpuin *, u8); +int sblive_miState2ByteKey(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon2(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *, u8); +int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *, u8); +int sblive_miStateSysExNorm(struct emu10k1_mpuin *, u8); +int sblive_miStateSysReal(struct emu10k1_mpuin *, u8); + +int emu10k1_mpuin_irqhandler(struct emu10k1_card *); +void emu10k1_mpuin_bh(unsigned long); +int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid); + +#endif /* _CARDMI_H */ diff --git a/drivers/sound/emu10k1/cardmo.c b/drivers/sound/emu10k1/cardmo.c new file mode 100644 index 000000000..e7a8612d2 --- /dev/null +++ b/drivers/sound/emu10k1/cardmo.c @@ -0,0 +1,229 @@ + +/* + ********************************************************************** + * cardmo.c - MIDI UART output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmo.h" + +/* Installs the IRQ handler for the MPU out port * + * and initialize parameters */ + +int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + + DPF(2, "emu10k1_mpuout_open()\n"); + + if (!(card_mpuout->status & FLAGS_AVAILABLE)) + return CTSTATUS_INUSE; + + /* Copy open info and mark channel as in use */ + card_mpuout->intr = 0; + card_mpuout->openinfo = *openinfo; + card_mpuout->status &= ~FLAGS_AVAILABLE; + card_mpuout->laststatus = 0x80; + card_mpuout->firstmidiq = NULL; + card_mpuout->lastmidiq = NULL; + + emu10k1_mpu_reset(card); + emu10k1_mpu_acquire(card); + + return CTSTATUS_SUCCESS; +} + +int emu10k1_mpuout_close(struct emu10k1_card *card) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + struct midi_queue *midiq; + struct midi_hdr *midihdr; + unsigned long flags; + + DPF(2, "emu10k1_mpuout_close()\n"); + + emu10k1_irq_disable(card, INTE_MIDITXENABLE); + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (card_mpuout->firstmidiq != NULL) { + midiq = card_mpuout->firstmidiq; + midihdr = (struct midi_hdr *) midiq->refdata; + + card_mpuout->firstmidiq = midiq->next; + + kfree(midihdr->data); + kfree(midihdr); + kfree(midiq); + } + + card_mpuout->lastmidiq = NULL; + + emu10k1_mpu_release(card); + + card_mpuout->status |= FLAGS_AVAILABLE; + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; +} + +/* If there isn't enough buffer space, reject Midi Buffer. * +* Otherwise, disable TX, create object to hold Midi * +* uffer, update buffer flags and other parameters * +* before enabling TX again. */ + +int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "emu10k1_mpuout_add_buffer()\n"); + + if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) + return CTSTATUS_SUCCESS; + + midihdr->flags |= MIDIBUF_INQUEUE; + midihdr->flags &= ~MIDIBUF_DONE; + + if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) { + /* Message lost */ + return CTSTATUS_NOMEMORY; + } + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->data; + + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + if (card_mpuout->firstmidiq == NULL) { + card_mpuout->firstmidiq = midiq; + card_mpuout->lastmidiq = midiq; + } else { + (card_mpuout->lastmidiq)->next = midiq; + card_mpuout->lastmidiq = midiq; + } + + card_mpuout->intr = 0; + + emu10k1_irq_enable(card, INTE_MIDITXENABLE); + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; +} + +void emu10k1_mpuout_bh(unsigned long refdata) +{ + struct emu10k1_card *card = (struct emu10k1_card *) refdata; + struct emu10k1_mpuout *card_mpuout = card->mpuout; + int cByteSent = 0; + int status; + struct midi_queue *midiq; + struct midi_queue *doneq = NULL; + unsigned long flags; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (card_mpuout->firstmidiq != NULL) { + midiq = card_mpuout->firstmidiq; + + while (cByteSent < 4 && midiq->sizeLeft) { + status = emu10k1_mpu_write_data(card, *midiq->midibyte); + + if (status == CTSTATUS_SUCCESS) { + ++cByteSent; + --midiq->sizeLeft; + ++midiq->midibyte; + } else { + DPF(2, "emu10k1_mpuoutDpcCallback error!!\n"); + } + } + + if (midiq->sizeLeft == 0) { + if (doneq == NULL) + doneq = midiq; + card_mpuout->firstmidiq = midiq->next; + } else + break; + } + + if (card_mpuout->firstmidiq == NULL) + card_mpuout->lastmidiq = NULL; + + if (doneq != NULL) { + while (doneq != card_mpuout->firstmidiq) { + unsigned long callback_msg[3]; + + midiq = doneq; + doneq = midiq->next; + + if (midiq->qtype) { + callback_msg[0] = 0; + callback_msg[1] = midiq->length; + callback_msg[2] = midiq->refdata; + + emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg); + } else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F) + card_mpuout->laststatus = (u8) midiq->refdata; + + kfree(midiq); + } + } + + if ((card_mpuout->firstmidiq != NULL) || cByteSent) { + card_mpuout->intr = 0; + emu10k1_irq_enable(card, INTE_MIDITXENABLE); + } + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return; +} + +int emu10k1_mpuout_irqhandler(struct emu10k1_card *card) +{ + struct emu10k1_mpuout *card_mpuout = card->mpuout; + + DPF(4, "emu10k1_mpuout_irqhandler\n"); + + card_mpuout->intr = 1; + emu10k1_irq_disable(card, INTE_MIDITXENABLE); + + tasklet_hi_schedule(&card_mpuout->tasklet); + + return CTSTATUS_SUCCESS; +} diff --git a/drivers/sound/emu10k1/cardmo.h b/drivers/sound/emu10k1/cardmo.h new file mode 100644 index 000000000..83871185e --- /dev/null +++ b/drivers/sound/emu10k1/cardmo.h @@ -0,0 +1,61 @@ +/* + ********************************************************************** + * cardmo.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMO_H +#define _CARDMO_H + +#include "icardmid.h" + +#define CARDMIDIOUT_STATE_DEFAULT 0x00000000 +#define CARDMIDIOUT_STATE_SUSPEND 0x00000001 + +struct emu10k1_mpuout +{ + u32 status; + u32 state; + volatile int intr; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + u8 laststatus; + struct tasklet_struct tasklet; + spinlock_t lock; + struct midi_openinfo openinfo; +}; + +int emu10k1_mpuout_open(struct emu10k1_card *, struct midi_openinfo *); +int emu10k1_mpuout_close(struct emu10k1_card *); +int emu10k1_mpuout_add_buffer(struct emu10k1_card *, struct midi_hdr *); + +int emu10k1_mpuout_irqhandler(struct emu10k1_card *); +void emu10k1_mpuout_bh(unsigned long); + +#endif /* _CARDMO_H */ diff --git a/drivers/sound/emu10k1/cardwi.c b/drivers/sound/emu10k1/cardwi.c new file mode 100644 index 000000000..fce4f0fdf --- /dev/null +++ b/drivers/sound/emu10k1/cardwi.c @@ -0,0 +1,472 @@ + +/* + ********************************************************************** + * cardwi.c - PCM input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "recmgr.h" +#include "audio.h" +#include "cardwi.h" + +void query_format(int recsrc, struct wave_format *wave_fmt) +{ + + switch (recsrc) { + case WAVERECORD_AC97: + + if ((wave_fmt->channels != 2) && (wave_fmt->channels != 1)) + wave_fmt->channels = 2; + + if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2) + wave_fmt->samplingrate = 0xBB80; + else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2) + wave_fmt->samplingrate = 0xAC44; + else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2) + wave_fmt->samplingrate = 0x7D00; + else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2) + wave_fmt->samplingrate = 0x5DC0; + else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2) + wave_fmt->samplingrate = 0x5622; + else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2) + wave_fmt->samplingrate = 0x3E80; + else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2) + wave_fmt->samplingrate = 0x2B11; + else + wave_fmt->samplingrate = 0x1F40; + + if ((wave_fmt->bitsperchannel != 16) && (wave_fmt->bitsperchannel != 8)) + wave_fmt->bitsperchannel = 16; + + break; + + case WAVERECORD_MIC: + wave_fmt->channels = 1; + wave_fmt->samplingrate = 0x1F40; + wave_fmt->bitsperchannel = 8; + break; + + case WAVERECORD_FX: + wave_fmt->channels = 2; + wave_fmt->samplingrate = 0xBB80; + wave_fmt->bitsperchannel = 16; + break; + + default: + break; + } + + return; +} + +static int alloc_recbuffer(struct wave_in *wave_in, u32 * bufsize, u8 ** buffer) +{ + u32 reqsize; + int i, j; + u32 size[4]; + + /* NOTE: record buffer size only can be certain sizes. If the requested + * size is not a nice size, use the smaller nearest size. The minimum size is 1k. */ + if (!wave_in->rec_ptr->is_16bit) + *bufsize <<= 1; + + if (*bufsize >= 0x10000) { + *bufsize = reqsize = 0x10000; + wave_in->rec_ptr->bufsize = 31; + } else { + reqsize = 0; + size[0] = 384; + size[1] = 448; + size[2] = 512; + size[3] = 640; + + for (i = 0; i < 8; i++) + for (j = 0; j < 4; j++) + if (*bufsize >= size[j]) { + reqsize = size[j]; + size[j] = size[j] * 2; + wave_in->rec_ptr->bufsize = i * 4 + j + 1; + } else + goto exitloop; + exitloop: + if (reqsize == 0) { + reqsize = 384; + wave_in->rec_ptr->bufsize = 1; + } + + *bufsize = reqsize; + } + + DPD(2, "bufsizereg: %x\n", wave_in->rec_ptr->bufsize); + + /* Recording buffer must be continuous and page-aligned */ + if ((wave_in->memhandle = emu10k1_alloc_memphysical(reqsize)) == NULL) + return CTSTATUS_ERROR; + + DPD(2, "recbufsize: %x\n", *bufsize); + + *buffer = (u8 *) wave_in->memhandle->virtaddx; + + return CTSTATUS_SUCCESS; +} + +static int get_recbuffer(struct emu10k1_card *card, struct wave_in *wave_in, u32 * size) +{ + u8 *buffer; + + wave_in->rec_ptr->card = card; + wave_in->rec_ptr->recpos = 0; + wave_in->rec_ptr->samplingrate = wave_in->wave_fmt.samplingrate; + wave_in->rec_ptr->is_stereo = (wave_in->wave_fmt.channels == 2) ? 1 : 0; + wave_in->rec_ptr->is_16bit = (wave_in->wave_fmt.bitsperchannel == 16) ? 1 : 0; + + /* Allocate buffer here */ + if (alloc_recbuffer(wave_in, size, &buffer) != CTSTATUS_SUCCESS) { + ERROR(); + return CTSTATUS_ERROR; + } + + /* recbufsize contains actual record buffer size */ + /* for 8 bit samples the size is twice the requested */ + /* value since we only make use of one in every two bytes */ + wave_in->rec_ptr->recbufsize = *size; + wave_in->rec_ptr->recbuffer = buffer; + wave_in->rec_ptr->busaddx = wave_in->memhandle->busaddx; + + return CTSTATUS_SUCCESS; +} + +static void dealloc_recbuffer(struct wave_in *wave_in) +{ + emu10k1_free_memphysical(wave_in->memhandle); + return; +} + +int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in; + struct wave_in **wave_in_tmp = NULL; + u32 buffsize, bytespersec, delay; + unsigned long flags; + + DPF(2, "emu10k1_wavein_open()\n"); + + if ((wave_in = (struct wave_in *) kmalloc(sizeof(struct wave_in), GFP_KERNEL)) == NULL) { + ERROR(); + return CTSTATUS_ERROR; + } + + wave_in->state = CARDWAVE_STATE_STOPPED; + wave_in->wave_fmt = wiinst->wave_fmt; + wave_in->memhandle = NULL; + wave_in->timer = NULL; + + switch (wiinst->recsrc) { + case WAVERECORD_AC97: + wave_in_tmp = &card->wavein->ac97; + break; + case WAVERECORD_MIC: + wave_in_tmp = &card->wavein->mic; + break; + case WAVERECORD_FX: + wave_in_tmp = &card->wavein->fx; + break; + default: + break; + } + + spin_lock_irqsave(&card->lock, flags); + if (*wave_in_tmp != NULL) { + spin_unlock_irqrestore(&card->lock, flags); + kfree(wave_in); + return CTSTATUS_ERROR; + } + + *wave_in_tmp = wave_in; + spin_unlock_irqrestore(&card->lock, flags); + + wiinst->wave_in = wave_in; + + if ((wave_in->rec_ptr = (struct record *) kmalloc(sizeof(struct record), GFP_KERNEL)) == NULL) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return CTSTATUS_ERROR; + } + + buffsize = wiinst->fragment_size * wiinst->numfrags; + + if (get_recbuffer(card, wave_in, &buffsize) != CTSTATUS_SUCCESS) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return CTSTATUS_ERROR; + } + + wiinst->fragment_size = buffsize / wiinst->numfrags; + + /* This callback size returned is the size in the play buffer. + * For 8-bit samples, callbacksize of user buffer should be + * half of the callbacksize in play buffer. */ + if (wave_in->wave_fmt.bitsperchannel == 8) + wiinst->fragment_size >>= 1; + + wave_in->callbacksize = wiinst->fragment_size; + + emu10k1_set_record_src(wave_in->rec_ptr, wiinst->recsrc); + + bytespersec = wave_in->wave_fmt.channels * (wave_in->wave_fmt.bitsperchannel >> 3) * (wave_in->wave_fmt.samplingrate); + delay = (48000 * wave_in->callbacksize) / bytespersec; + + if ((wave_in->timer = emu10k1_timer_install(card, emu10k1_wavein_bh, (unsigned long) wave_dev, delay / 2)) == NULL) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} + +void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wave_in *wave_in = wave_dev->wiinst->wave_in; + unsigned long flags; + + if (wave_in->state != CARDWAVE_STATE_STOPPED) + emu10k1_wavein_stop(wave_dev); + + if (wave_in->timer != NULL) + emu10k1_timer_uninstall(card, wave_in->timer); + + if (wave_in->memhandle != NULL) + dealloc_recbuffer(wave_in); + + if (wave_in->rec_ptr != NULL) + kfree(wave_in->rec_ptr); + + spin_lock_irqsave(&card->lock, flags); + switch (wave_dev->wiinst->recsrc) { + case WAVERECORD_AC97: + card->wavein->ac97 = NULL; + break; + case WAVERECORD_MIC: + card->wavein->mic = NULL; + break; + case WAVERECORD_FX: + card->wavein->fx = NULL; + break; + default: + break; + } + spin_unlock_irqrestore(&card->lock, flags); + + kfree(wave_in); + wave_dev->wiinst->wave_in = NULL; + + return; +} + +void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev) +{ + struct wave_in *wave_in = wave_dev->wiinst->wave_in; + + DPF(2, "emu10k1_wavein_start()\n"); + + if (wave_in->state == CARDWAVE_STATE_STARTED) + return; + + emu10k1_start_record(wave_in->rec_ptr); + wave_in->state = CARDWAVE_STATE_STARTED; + + emu10k1_timer_enable(wave_dev->card, wave_in->timer); + + return; +} + +void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct wave_in *wave_in = wave_dev->wiinst->wave_in; + + DPF(2, "emu10k1_wavein_stop()\n"); + + emu10k1_stop_record(wave_in->rec_ptr); + emu10k1_timer_disable(wave_dev->card, wave_in->timer); + + wave_in->rec_ptr->recpos = 0; + wave_in->state = CARDWAVE_STATE_STOPPED; + + return; +} + +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in = wiinst->wave_in; + u32 bytespersec, delay; + + DPF(2, "emu10k1_wavein_setformat()\n"); + + query_format(wiinst->recsrc, &wiinst->wave_fmt); + + if (!wave_in) + return CTSTATUS_SUCCESS; + + if (wave_in->state == CARDWAVE_STATE_STARTED) { + wiinst->wave_fmt = wave_in->wave_fmt; + return CTSTATUS_SUCCESS; + } + + if ((wave_in->wave_fmt.samplingrate != wiinst->wave_fmt.samplingrate) + || (wave_in->wave_fmt.bitsperchannel != wiinst->wave_fmt.bitsperchannel) + || (wave_in->wave_fmt.channels != wiinst->wave_fmt.channels)) { + + emu10k1_timer_uninstall(card, wave_in->timer); + + wave_in->wave_fmt = wiinst->wave_fmt; + + bytespersec = wave_in->wave_fmt.channels * (wave_in->wave_fmt.bitsperchannel >> 3) * (wave_in->wave_fmt.samplingrate); + delay = (48000 * wave_in->callbacksize) / bytespersec; + + if ((wave_in->timer = emu10k1_timer_install(card, emu10k1_wavein_bh, (unsigned long) wave_dev, delay / 2)) == NULL) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return CTSTATUS_ERROR; + } + } + + return CTSTATUS_SUCCESS; +} + +void emu10k1_wavein_getxfersize(struct wave_in *wave_in, u32 * size, u32 * curpos) +{ + struct record *rec_ptr = wave_in->rec_ptr; + + /* Get position of current address, this is in no. of bytes in play buffer */ + emu10k1_wavein_getcontrol(wave_in, WAVECURPOS, curpos); + + *size = *curpos - rec_ptr->recpos; + + /* Recpos is the actual position in user buffer and play buffer */ + if (*curpos < rec_ptr->recpos) + *size += rec_ptr->recbufsize; + + if (!rec_ptr->is_16bit) + *size >>= 1; + + return; +} + +static void copy_s16_to_u8(u8 * dstbuf, s16 * srcbuf, u32 size) +{ + u16 sample; + u8 byte; + + while (size--) { + sample = (*srcbuf) + 32767; + byte = (u8) (sample >> 8); + copy_to_user(dstbuf, &byte, 1); + dstbuf++; + srcbuf++; + } +} + +/* transfer the data from the wave device. */ +void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 * data, u32 * size) +{ + struct wave_in *wave_in = wiinst->wave_in; + struct record *rec_ptr = wave_in->rec_ptr; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min(rec_ptr->recbufsize * (rec_ptr->is_16bit + 1) / 2, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&wiinst->lock, flags); + + sizetocopy_now = (rec_ptr->recbufsize - rec_ptr->recpos) * (rec_ptr->is_16bit + 1) / 2; + + start = rec_ptr->recpos; + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + rec_ptr->recpos = sizetocopy * 2 / (rec_ptr->is_16bit + 1); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (rec_ptr->is_16bit) { + copy_to_user(data, rec_ptr->recbuffer + start, sizetocopy_now); + copy_to_user(data + sizetocopy_now, rec_ptr->recbuffer, sizetocopy); + } else { + copy_s16_to_u8(data, (s16 *) (rec_ptr->recbuffer + start), sizetocopy_now); + copy_s16_to_u8(data + sizetocopy_now, (s16 *) rec_ptr->recbuffer, sizetocopy); + } + } else { + if (sizetocopy == sizetocopy_now) + rec_ptr->recpos = 0; + else + rec_ptr->recpos += sizetocopy * 2 / (rec_ptr->is_16bit + 1); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (rec_ptr->is_16bit) + copy_to_user(data, rec_ptr->recbuffer + start, sizetocopy); + else + copy_s16_to_u8(data, (s16 *) (rec_ptr->recbuffer + start), sizetocopy); + } + + return; +} + +/* get the specified control value of the wave device. */ + +int emu10k1_wavein_getcontrol(struct wave_in *wave_in, u32 ctrlid, u32 * value) +{ + switch (ctrlid) { + case WAVECURPOS: + /* There is no actual start yet */ + if (wave_in->state == CARDWAVE_STATE_STOPPED) { + *value = 0; + } else { + /* value is in byte units */ + *value = sblive_readptr(wave_in->rec_ptr->card, wave_in->rec_ptr->bufidxreg, 0); + } + + break; + + default: + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} diff --git a/drivers/sound/emu10k1/cardwi.h b/drivers/sound/emu10k1/cardwi.h new file mode 100644 index 000000000..2d781033d --- /dev/null +++ b/drivers/sound/emu10k1/cardwi.h @@ -0,0 +1,92 @@ +/* + ********************************************************************** + * cardwi.h -- header file for card wave input functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ +#ifndef _CARDWI_H +#define _CARDWI_H + +#include "icardwav.h" + +struct wave_in +{ + struct list_head list; + + u32 state; + struct record *rec_ptr; + struct memhandle *memhandle; + struct emu_timer *timer; + u32 callbacksize; + struct wave_format wave_fmt; +}; + +struct wiinst +{ + struct wave_in *wave_in; + struct wave_format wave_fmt; + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + wait_queue_head_t wait_queue; + int mapped; + u32 total_recorded; + u32 blocks; + u32 curpos; + spinlock_t lock; + u8 recsrc; +}; + +struct emu10k1_wavein +{ + struct wave_in *ac97; + struct wave_in *mic; + struct wave_in *fx; + + u8 recsrc; +}; + + +#define WAVEIN_MAXBUFSIZE 65536 +#define WAVEIN_MINBUFSIZE 368 + +#define WAVEIN_DEFAULTFRAGLEN 100 +#define WAVEIN_DEFAULTBUFLEN 1000 + +#define WAVEIN_MINFRAGSHIFT 8 + +int emu10k1_wavein_open(struct emu10k1_wavedevice *); +void emu10k1_wavein_close(struct emu10k1_wavedevice *); +void emu10k1_wavein_start(struct emu10k1_wavedevice *); +void emu10k1_wavein_stop(struct emu10k1_wavedevice *); +void emu10k1_wavein_getxfersize(struct wave_in *, u32 *, u32 *); +void emu10k1_wavein_xferdata(struct wiinst *, u8 *, u32 *); +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *); +int emu10k1_wavein_getcontrol(struct wave_in *, u32, u32 *); + + +#endif /* _CARDWI_H */ diff --git a/drivers/sound/emu10k1/cardwo.c b/drivers/sound/emu10k1/cardwo.c new file mode 100644 index 000000000..2423df032 --- /dev/null +++ b/drivers/sound/emu10k1/cardwo.c @@ -0,0 +1,755 @@ + +/* + ********************************************************************** + * cardwo.c - PCM output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardwo.h" +#include "audio.h" + +/* Volume calcs */ + +static int set_volume_instance(struct emu10k1_waveout *card_waveout, struct wave_out *wave_out, struct voice_param *left) +{ + /* only applicable for playback */ + u32 volL, volR, vol = 0; + + volL = (wave_out->localvol & 0xffff); + volR = ((wave_out->localvol >> 16) & 0xffff); + + if (wave_out->globalvolFactor) { + volL = ((u32) (((u16) card_waveout->globalvol & 0xffff) * (u16) volL)) / 0xffff; + volR = ((u32) (((u16) (card_waveout->globalvol >> 16) & 0xffff) * ((u16) volR))) / 0xffff; + } + + /* BIG ASSUMPTION HERE THAT DEFAULT WAVE PAN/AUX IS 0xff/0xff */ + /* New volume and pan */ + + if (volL == volR) { + vol = volL; + left->send_c = 0xff; + left->send_b = 0xff; + } else { + if (volL > volR) { + vol = volL; + left->send_c = 0xff; + left->send_b = (char) ((volR * 255) / vol); + } else { + vol = volR; + left->send_b = 0xff; + left->send_c = (char) ((volL * 255) / vol); + } + } + + left->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2); + + return vol; +} + +static void query_format(struct wave_format *wave_fmt) +{ + if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2)) + wave_fmt->channels = 2; + + if (wave_fmt->samplingrate >= 0x2EE00) + wave_fmt->samplingrate = 0x2EE00; + + if ((wave_fmt->bitsperchannel != 8) && (wave_fmt->bitsperchannel != 16)) + wave_fmt->bitsperchannel = 16; + + return; +} + +static int alloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size, void ***buffer) +{ + u32 numpages, reqsize, pageindex, pagecount; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + unsigned long busaddx; + int i; + + reqsize = *size; + numpages = reqsize / PAGE_SIZE; + + /* If size is not a multiple of PAGE_SIZE then we need to round up */ + if (reqsize % PAGE_SIZE) + numpages += 1; + + DPD(2, "requested pages is: %d\n", numpages); + + wavexferbuf->numpages = numpages; + + /* Only for playback, request for emu address space */ + /* Support non page-aligned buffer, don't need interpolation page */ + + if ((wave_out->emupageindex = emu10k1_addxmgr_alloc(numpages * PAGE_SIZE, card)) < 0) + return CTSTATUS_ERROR; + + if ((wave_out->pagetable = (void **) kmalloc(sizeof(void *) * numpages, GFP_KERNEL)) == NULL) + return CTSTATUS_ERROR; + + /* Fill in virtual memory table */ + for (pagecount = 0; pagecount < numpages; pagecount++) { + if ((wave_out->pagetable[pagecount] = (void *) __get_free_page(GFP_KERNEL)) == NULL) { + wavexferbuf->numpages = pagecount; + return CTSTATUS_ERROR; + } + + DPD(2, "Virtual Addx: %p\n", wave_out->pagetable[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + busaddx = virt_to_bus((u8 *) wave_out->pagetable[pagecount] + i * EMUPAGESIZE); + + DPD(3, "Bus Addx: %lx\n", busaddx); + + pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + + ((u32 *) card->virtualpagetable->virtaddx)[pageindex] = ((u32) busaddx * 2) | pageindex; + } + } + + *buffer = wave_out->pagetable; + + return CTSTATUS_SUCCESS; +} + +static int get_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size) +{ + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + void **buffer; + + wavexferbuf->xferpos = 0; + wavexferbuf->silence_xferpos = 0; + wavexferbuf->stopposition = 0; + wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0; + wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0; + wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1); + + if (alloc_xferbuffer(card, wave_out, size, &buffer) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + /* xferbufsize contains actual transfer buffer size */ + wavexferbuf->xferbufsize = *size; + wavexferbuf->xferbuffer = buffer; + + return CTSTATUS_SUCCESS; +} + +static void dealloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out) +{ + u32 pagecount, pageindex; + int i; + + if (wave_out->pagetable != NULL) { + for (pagecount = 0; pagecount < wave_out->wavexferbuf->numpages; pagecount++) { + free_page((unsigned long) wave_out->pagetable[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + ((u32 *) card->virtualpagetable->virtaddx)[pageindex] = (card->silentpage->busaddx * 2) | pageindex; + } + } + kfree(wave_out->pagetable); + } + + emu10k1_addxmgr_free(card, wave_out->emupageindex); + + return; +} + +static int get_voice(struct emu10k1_card *card, struct wave_out *wave_out, int device) +{ + struct emu10k1_waveout *card_waveout = card->waveout; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + struct voice_allocdesc voice_allocdesc; + struct voice_param *left, *right; + u32 size; + + /* Allocate voices here, if no voices available, return error. + * Init voice_allocdesc first.*/ + + voice_allocdesc.usage = VOICEMGR_USAGE_PLAYBACK; + + voice_allocdesc.flags = 0; + + if (device == 1) + voice_allocdesc.flags |= VOICEMGR_FLAGS_FXRT2; + + if (wave_out->wave_fmt.channels == 1) + voice_allocdesc.flags |= VOICEMGR_FLAGS_MONO; + + if (wave_out->wave_fmt.bitsperchannel == 16) + voice_allocdesc.flags |= VOICEMGR_FLAGS_16BIT; + + if ((wave_out->voice = emu10k1_voice_alloc(&card->voicemgr, &voice_allocdesc)) == NULL) + return CTSTATUS_ERROR; + + /* voice initialization */ + + left = &wave_out->voice->params; + + /* Calculate pitch */ + left->initial_pitch = (u16) (srToPitch(wave_out->wave_fmt.samplingrate) >> 8); + + DPD(2, "Initial pitch --> %x\n", left->initial_pitch); + + /* Easy way out.. gotta calculate value */ + left->pitch_target = 0; + left->volume_target = 0; + left->FC_target = 0; + + left->byampl_env_sustain = 0x7f; + left->byampl_env_decay = 0x7f; + + if (wave_out->globalreverbFactor) { + u8 t = (card_waveout->globalreverb & 0xff) + (wave_out->localreverb & 0xff); + + left->send_a = (t > 255) ? 255 : t; + } else { + left->send_a = 0; + } + + if (wave_out->globalchorusFactor) { + u8 t = (card_waveout->globalchorus & 0xff) + (wave_out->localchorus & 0xff); + + left->send_d = (t > 255) ? 255 : t; + } else { + left->send_d = 0; + } + + set_volume_instance(card_waveout, wave_out, left); + + left->pan_target = left->send_c; + left->aux_target = left->send_b; + + size = wavexferbuf->xferbufsize / wavexferbuf->bytespersample; + left->start = 2 * (wave_out->emupageindex << 11) / wavexferbuf->bytespersample; + left->end = left->start + size; + left->startloop = left->start; + left->endloop = left->end; + + if (wave_out->voice->linked_voice) { + DPF(2, "is stereo\n"); + right = &wave_out->voice->linked_voice->params; + + right->initial_pitch = left->initial_pitch; + + /* Easy way out.. gotta calculate value */ + right->pitch_target = 0; + right->volume_target = 0; + right->FC_target = 0; + + right->byampl_env_sustain = 0x7f; + right->byampl_env_decay = 0x7f; + + right->send_d = left->send_d; + right->send_a = left->send_a; + + /* Left output of right channel is always zero */ + right->send_c = 0; + + /* Update right channel aux */ + right->pan_target = 0; + right->send_b = left->send_b; + right->aux_target = right->send_b; + + /* Zero out right output of left channel */ + left->send_b = 0; + left->aux_target = 0; + + /* Update right channel attenuation */ + right->initial_attn = left->initial_attn; + + right->start = left->start; + right->end = left->end; + right->startloop = left->startloop; + right->endloop = left->endloop; + + } + + DPD(2, "voice: start=%x, end=%x, startloop=%x, endloop=%x\n", left->start, left->end, left->startloop, left->endloop); + + return CTSTATUS_SUCCESS; +} + +int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out; + u32 bytespersec, delay; + u32 buffsize; + + DPF(2, "emu10k1_waveout_open()\n"); + + if ((wave_out = (struct wave_out *) kmalloc(sizeof(struct wave_out), GFP_KERNEL)) == NULL) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + woinst->wave_out = wave_out; + + /* Init channel object */ + wave_out->state = CARDWAVE_STATE_STOPPED; + wave_out->wave_fmt = woinst->wave_fmt; + wave_out->voice = NULL; + wave_out->emupageindex = -1; + wave_out->wavexferbuf = NULL; + wave_out->pagetable = NULL; + wave_out->timer = NULL; + + /* Assign default local volume */ + /* FIXME: Should we be maxing the initial values like this? */ + wave_out->localvol = 0xffffffff; + wave_out->localreverb = 0xffffffff; + wave_out->localchorus = 0xffffffff; + wave_out->globalvolFactor = 0xffff; + wave_out->globalreverbFactor = 0xffff; + wave_out->globalchorusFactor = 0xffff; + + wave_out->setpos = 0; + wave_out->position = 0; + + wave_out->fill_silence = 0; + + if ((wave_out->wavexferbuf = (struct wave_xferbuf *) kmalloc(sizeof(struct wave_xferbuf), GFP_KERNEL)) == NULL) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + buffsize = woinst->fragment_size * woinst->numfrags; + + if (get_xferbuffer(card, wave_out, &buffsize) != CTSTATUS_SUCCESS) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + woinst->fragment_size = buffsize / woinst->numfrags; + wave_out->callbacksize = woinst->fragment_size; + + if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate); + delay = (48000 * wave_out->callbacksize) / bytespersec; + + if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} + +void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wave_out *wave_out = wave_dev->woinst->wave_out; + + DPF(2, "emu10k1_waveout_close()\n"); + + if (wave_out->state != CARDWAVE_STATE_STOPPED) + emu10k1_waveout_stop(wave_dev); + + if (wave_out->timer != NULL) + emu10k1_timer_uninstall(card, wave_out->timer); + + if (wave_out->voice != NULL) + emu10k1_voice_free(&card->voicemgr, wave_out->voice); + + if (wave_out->emupageindex >= 0) + dealloc_xferbuffer(card, wave_out); + + if (wave_out->wavexferbuf != NULL) + kfree(wave_out->wavexferbuf); + + kfree(wave_out); + wave_dev->woinst->wave_out = NULL; + + return; +} + +int emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wave_out *wave_out = wave_dev->woinst->wave_out; + u32 start, startPosition; + + DPF(2, "emu10k1_waveout_start()\n"); + + /* If already started, return success */ + if (wave_out->state == CARDWAVE_STATE_STARTED) + return CTSTATUS_SUCCESS; + + if (wave_out->state == CARDWAVE_STATE_STOPPED && wave_out->setpos) + startPosition = wave_out->position / (wave_out->wavexferbuf->bytespersample); + else + startPosition = wave_out->wavexferbuf->stopposition; + + start = wave_out->voice->params.start; + wave_out->voice->params.start += startPosition; + + DPD(2, "CA is %x\n", wave_out->voice->params.start); + + emu10k1_voice_playback_setup(wave_out->voice); + + wave_out->voice->params.start = start; + + /* Actual start */ + emu10k1_voice_start(wave_out->voice); + + wave_out->state = CARDWAVE_STATE_STARTED; + wave_out->setpos = 0; + + emu10k1_timer_enable(card, wave_out->timer); + + return CTSTATUS_SUCCESS; +} + +int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + u32 bytespersec, delay; + + DPF(2, "emu10k1_waveout_setformat()\n"); + + query_format(&woinst->wave_fmt); + + if (wave_out == NULL) + return CTSTATUS_SUCCESS; + + if (wave_out->state == CARDWAVE_STATE_STARTED) { + woinst->wave_fmt = wave_out->wave_fmt; + return CTSTATUS_SUCCESS; + } + + if ((wave_out->wave_fmt.samplingrate != woinst->wave_fmt.samplingrate) + || (wave_out->wave_fmt.bitsperchannel != woinst->wave_fmt.bitsperchannel) + || (wave_out->wave_fmt.channels != woinst->wave_fmt.channels)) { + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + + emu10k1_timer_uninstall(card, wave_out->timer); + + emu10k1_voice_free(&card->voicemgr, wave_out->voice); + + wave_out->wave_fmt = woinst->wave_fmt; + wave_out->timer = NULL; + + wavexferbuf->xferpos = 0; + wavexferbuf->silence_xferpos = 0; + wavexferbuf->stopposition = 0; + wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0; + wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0; + wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1); + + if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + + bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate); + delay = (48000 * wave_out->callbacksize) / bytespersec; + + if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return CTSTATUS_ERROR; + } + } + + return CTSTATUS_SUCCESS; +} + +void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wave_out *wave_out = wave_dev->woinst->wave_out; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u32 samples = 32; + u32 position; + + DPF(2, "emu10k1_waveout_stop()\n"); + + if (wave_out->state == CARDWAVE_STATE_STOPPED) + return; + + emu10k1_timer_disable(card, wave_out->timer); + + /* Stop actual voice */ + emu10k1_voice_stop(wave_out->voice); + + /* Save the stop position */ + emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, &wavexferbuf->stopposition); + + wavexferbuf->stopposition -= wave_out->voice->params.start; + + /* Refer to voicemgr.c, CA is not started at zero. We need to take this into account. */ + position = wavexferbuf->stopposition * wavexferbuf->bytespersample; + + if (!wavexferbuf->is_16bit) + samples <<= 1; + + if (wavexferbuf->is_stereo) + samples <<= 1; + + samples -= 4; + + if (position >= samples * (wavexferbuf->is_16bit + 1)) + position -= samples * (wavexferbuf->is_16bit + 1); + else + position += wavexferbuf->xferbufsize - samples * (wavexferbuf->is_16bit + 1); + + wavexferbuf->stopposition = position / wavexferbuf->bytespersample; + + DPD(2, "position is %x\n", wavexferbuf->stopposition); + + wave_out->state = CARDWAVE_STATE_STOPPED; + wave_out->setpos = 0; + wave_out->position = 0; + + return; +} + +void emu10k1_waveout_getxfersize(struct wave_out *wave_out, u32 * size, u32 * pending, u32 * curpos) +{ + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + + /* Get position of current address, this is in no. of bytes in play buffer */ + emu10k1_waveout_getcontrol(wave_out, WAVECURPOS, curpos); + + if ((*curpos > wavexferbuf->silence_xferpos) + || ((*curpos == wavexferbuf->silence_xferpos) + && (wave_out->state == CARDWAVE_STATE_STARTED)) + || ((*curpos == wavexferbuf->silence_xferpos) && (wavexferbuf->silence_xferpos != 0) + && (wave_out->state == CARDWAVE_STATE_STOPPED))) { + *size = *curpos - wavexferbuf->silence_xferpos; + *pending = wavexferbuf->xferbufsize - *size; + } else { + *pending = wavexferbuf->silence_xferpos - *curpos; + *size = wavexferbuf->xferbufsize - *pending; + } + + if (wavexferbuf->silence_xferpos != wavexferbuf->xferpos) { + if (*pending < wave_out->callbacksize) { + wave_out->fill_silence = 2; + *pending = 0; + *size = wavexferbuf->xferbufsize; + wavexferbuf->xferpos = *curpos; + } else { + if (wave_out->fill_silence == 2) { + *pending = 0; + *size = wavexferbuf->xferbufsize; + wavexferbuf->xferpos = *curpos; + } else { + *pending -= wave_out->callbacksize; + *size += wave_out->callbacksize; + } + } + } else { + if (*pending < wave_out->callbacksize) + wave_out->fill_silence = 1; + else + wave_out->fill_silence = 0; + } + + return; +} + +static void copy_block(u32 dst, u8 * src, u32 len, void **pt) +{ + int i, j, k; + + i = dst / PAGE_SIZE; + j = dst % PAGE_SIZE; + k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len; + copy_from_user(pt[i] + j, src, k); + len -= k; + while (len >= PAGE_SIZE) { + copy_from_user(pt[++i], src + k, PAGE_SIZE); + k += PAGE_SIZE; + len -= PAGE_SIZE; + } + copy_from_user(pt[++i], src + k, len); + + return; +} + +static void fill_block(u32 dst, u8 val, u32 len, void **pt) +{ + int i, j, k; + + i = dst / PAGE_SIZE; + j = dst % PAGE_SIZE; + k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len; + memset(pt[i] + j, val, k); + len -= k; + while (len >= PAGE_SIZE) { + memset(pt[++i], val, PAGE_SIZE); + len -= PAGE_SIZE; + } + memset(pt[++i], val, len); + + return; +} + +void emu10k1_waveout_xferdata(struct woinst *woinst, u8 * data, u32 * size) +{ + struct wave_out *wave_out = woinst->wave_out; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min(wavexferbuf->xferbufsize, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&woinst->lock, flags); + + sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->xferpos; + + start = wavexferbuf->xferpos; + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + wavexferbuf->xferpos = sizetocopy; + wavexferbuf->silence_xferpos = wavexferbuf->xferpos; + spin_unlock_irqrestore(&woinst->lock, flags); + + copy_block(start, data, sizetocopy_now, wavexferbuf->xferbuffer); + copy_block(0, data + sizetocopy_now, sizetocopy, wavexferbuf->xferbuffer); + } else { + if (sizetocopy == sizetocopy_now) + wavexferbuf->xferpos = 0; + else + wavexferbuf->xferpos += sizetocopy; + + wavexferbuf->silence_xferpos = wavexferbuf->xferpos; + spin_unlock_irqrestore(&woinst->lock, flags); + + copy_block(start, data, sizetocopy, wavexferbuf->xferbuffer); + } + + return; +} + +void emu10k1_waveout_fillsilence(struct woinst *woinst) +{ + struct wave_out *wave_out = woinst->wave_out; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u16 filldata; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = wave_out->callbacksize; + + if (wave_out->wave_fmt.bitsperchannel == 8) + filldata = 0x8080; + else + filldata = 0x0000; + + spin_lock_irqsave(&woinst->lock, flags); + + sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->silence_xferpos; + start = wavexferbuf->silence_xferpos; + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + wavexferbuf->silence_xferpos = sizetocopy; + spin_unlock_irqrestore(&woinst->lock, flags); + fill_block(start, filldata, sizetocopy_now, wavexferbuf->xferbuffer); + fill_block(0, filldata, sizetocopy, wavexferbuf->xferbuffer); + } else { + if (sizetocopy == sizetocopy_now) + wavexferbuf->silence_xferpos = 0; + else + wavexferbuf->silence_xferpos += sizetocopy; + + spin_unlock_irqrestore(&woinst->lock, flags); + + fill_block(start, filldata, sizetocopy, wavexferbuf->xferbuffer); + } + + return; +} + +/* get the specified control value of the wave device. */ + +int emu10k1_waveout_getcontrol(struct wave_out *wave_out, u32 ctrl_id, u32 * value) +{ + switch (ctrl_id) { + case WAVECURPOS: + /* There is no actual start yet */ + if (wave_out->state == CARDWAVE_STATE_STOPPED) { + if (wave_out->setpos) + *value = wave_out->position; + else + *value = wave_out->wavexferbuf->stopposition * wave_out->wavexferbuf->bytespersample; + } else { + emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, value); + + *value -= wave_out->voice->params.start; + + /* Get number of bytes in play buffer per channel. + * If 8 bit mode is enabled, this needs to be changed. */ + { + u32 samples = 64 * (wave_out->wavexferbuf->is_stereo + 1); + + *value *= wave_out->wavexferbuf->bytespersample; + + /* Refer to voicemgr.c, CA is not started at zero. + * We need to take this into account. */ + + samples -= 4 * (wave_out->wavexferbuf->is_16bit + 1); + + if (*value >= samples) + *value -= samples; + else + *value += wave_out->wavexferbuf->xferbufsize - samples; + } + } + + break; + default: + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} diff --git a/drivers/sound/emu10k1/cardwo.h b/drivers/sound/emu10k1/cardwo.h new file mode 100644 index 000000000..20391432a --- /dev/null +++ b/drivers/sound/emu10k1/cardwo.h @@ -0,0 +1,118 @@ +/* + ********************************************************************** + * cardwo.h -- header file for card wave out functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDWO_H +#define _CARDWO_H + +#include "icardwav.h" + +struct wave_xferbuf +{ + u32 xferpos; + u32 silence_xferpos; + u32 xferbufsize; /* transfer buffer size */ + u32 numpages; /* number of pages in transfer buffer */ + void **xferbuffer; /* pointer to the transfer buffer */ + int is_stereo; + int is_16bit; + int bytespersample; + u32 stopposition; +}; + +struct wave_out +{ + u32 state; + struct emu_voice *voice; + int emupageindex; + struct emu_timer *timer; + struct wave_xferbuf *wavexferbuf; + void **pagetable; + u32 callbacksize; + u32 localvol; + u32 localreverb; + u32 localchorus; + u32 globalvolFactor; + u32 globalreverbFactor; + u32 globalchorusFactor; + int setpos; + u32 position; + struct wave_format wave_fmt; + int fill_silence; +}; + +/* setting this to other than a power of two + may break some applications */ +#define WAVEOUT_MAXBUFSIZE 32768 +#define WAVEOUT_MINBUFSIZE 64 + +#define WAVEOUT_DEFAULTFRAGLEN 100 /* Time to play a fragment in ms (latency) */ +#define WAVEOUT_DEFAULTBUFLEN 1000 /* Time to play the entire buffer in ms */ + +#define WAVEOUT_MINFRAGSHIFT 4 + +struct woinst +{ + struct wave_out *wave_out; + struct wave_format wave_fmt; + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + wait_queue_head_t wait_queue; + int mapped; + u32 total_copied; + u32 total_played; + u32 blocks; + u32 curpos; + u32 device; + spinlock_t lock; +}; + +struct emu10k1_waveout +{ + u32 globalvol; + u32 mute; + u32 left; + u32 right; + u32 globalreverb; + u32 globalchorus; +}; + +int emu10k1_waveout_open(struct emu10k1_wavedevice *); +void emu10k1_waveout_close(struct emu10k1_wavedevice *); +int emu10k1_waveout_start(struct emu10k1_wavedevice *); +void emu10k1_waveout_stop(struct emu10k1_wavedevice *); +void emu10k1_waveout_getxfersize(struct wave_out *, u32 *, u32 *, u32 *); +void emu10k1_waveout_xferdata(struct woinst*, u8*, u32 *); +void emu10k1_waveout_fillsilence(struct woinst*); +int emu10k1_waveout_setformat(struct emu10k1_wavedevice*); +int emu10k1_waveout_getcontrol(struct wave_out*, u32, u32 *); + +#endif /* _CARDWO_H */ diff --git a/drivers/sound/emu10k1/efxmgr.c b/drivers/sound/emu10k1/efxmgr.c new file mode 100644 index 000000000..1b4e01a21 --- /dev/null +++ b/drivers/sound/emu10k1/efxmgr.c @@ -0,0 +1,34 @@ + +/* + ********************************************************************** + * sblive_fx.c + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "efxmgr.h" diff --git a/drivers/sound/emu10k1/efxmgr.h b/drivers/sound/emu10k1/efxmgr.h new file mode 100644 index 000000000..eaa9dffa2 --- /dev/null +++ b/drivers/sound/emu10k1/efxmgr.h @@ -0,0 +1,43 @@ +/* + ********************************************************************** + * sblive_fx.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _EFXMGR_H +#define _EFXMGR_H + +#define WRITE_EFX(a, b, c) sblive_writeptr((a), MICROCODEBASE + (b), 0, (c)) + +#define OP(op, z, w, x, y) \ + do { WRITE_EFX(card, (pc) * 2, ((x) << 10) | (y)); \ + WRITE_EFX(card, (pc) * 2 + 1, ((op) << 20) | ((z) << 10) | (w)); \ + ++pc; } while (0) + + +#endif /* _EFXMGR_H */ diff --git a/drivers/sound/emu10k1/emu_wrapper.h b/drivers/sound/emu10k1/emu_wrapper.h new file mode 100644 index 000000000..f0010d40e --- /dev/null +++ b/drivers/sound/emu10k1/emu_wrapper.h @@ -0,0 +1,11 @@ +#ifndef __EMU_WRAPPER_H +#define __EMU_WRAPPER_H + +#include <linux/wrapper.h> + +#define UP_INODE_SEM(a) +#define DOWN_INODE_SEM(a) + +#define GET_INODE_STRUCT() + +#endif diff --git a/drivers/sound/emu10k1/emuadxmg.c b/drivers/sound/emu10k1/emuadxmg.c new file mode 100644 index 000000000..94243c874 --- /dev/null +++ b/drivers/sound/emu10k1/emuadxmg.c @@ -0,0 +1,101 @@ + +/* + ********************************************************************** + * emuadxmg.c - Address space manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +/* Allocates emu address space */ + +int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card) +{ + u16 *pagetable = card->emupagetable; + u16 index = 0; + u16 numpages; + unsigned long flags; + + /* Convert bytes to pages */ + numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0); + + while (index < (MAXPAGES - RESERVED - 1)) { + if (pagetable[index] & 0x8000) { + /* This block of pages is in use, jump to the start of the next block. */ + index += (pagetable[index] & 0x7fff); + } else { + /* Found free block */ + if (pagetable[index] >= numpages) { + spin_lock_irqsave(&card->lock, flags); + + /* Block is large enough */ + + /* If free block is larger than the block requested + * then adjust the size of the block remaining */ + if (pagetable[index] > numpages) + pagetable[index + numpages] = pagetable[index] - numpages; + + pagetable[index] = (numpages | 0x8000); /* Mark block as used */ + + spin_unlock_irqrestore(&card->lock, flags); + + return index; + } else { + /* Block too small, jump to the start of the next block */ + index += pagetable[index]; + } + } + } + + return -1; +} + +/* Frees a previously allocated emu address space. */ + +void emu10k1_addxmgr_free(struct emu10k1_card *card, int index) +{ + u16 *pagetable = card->emupagetable; + u16 origsize = 0; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + if (pagetable[index] & 0x8000) { + /* Block is allocated - mark block as free */ + origsize = pagetable[index] & 0x7fff; + pagetable[index] = origsize; + + /* If next block is free, we concat both blocks */ + if (!(pagetable[index + origsize] & 0x8000)) + pagetable[index] += pagetable[index + origsize] & 0x7fff; + } + + spin_unlock_irqrestore(&card->lock, flags); + + return; +} diff --git a/drivers/sound/emu10k1/hwaccess.c b/drivers/sound/emu10k1/hwaccess.c new file mode 100644 index 000000000..fda2baa22 --- /dev/null +++ b/drivers/sound/emu10k1/hwaccess.c @@ -0,0 +1,430 @@ + +/* + ********************************************************************** + * hwaccess.c -- Hardware access layer + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * December 9, 1999 Jon Taylor rewrote the I/O subsystem + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "icardmid.h" + +/************************************************************************* +* Function : srToPitch * +* Input : sampleRate - sampling rate * +* Return : pitch value * +* About : convert sampling rate to pitch * +* Note : for 8010, sampling rate is at 48kHz, this function should * +* be changed. * +*************************************************************************/ +u32 srToPitch(u32 sampleRate) +{ + int i; + + /* FIXME: These tables should be defined in a headerfile */ + 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 + }; + + if (sampleRate == 0) + return (0); /* Bail out if no leading "1" */ + + sampleRate *= 11185; /* Scale 48000 to 0x20002380 */ + + for (i = 31; i > 0; i--) { + if (sampleRate & 0x80000000) { /* Detect leading "1" */ + return (u32) (((s32) (i - 15) << 20) + + logMagTable[0x7f & (sampleRate >> 24)] + + (0x7f & (sampleRate >> 17)) * logSlopeTable[0x7f & (sampleRate >> 24)]); + } + sampleRate = sampleRate << 1; + } + + DPF(2, "srToPitch: BUG!\n"); + return 0; /* Should never reach this point */ +} + +/* Returns an attenuation based upon a cumulative volume value */ + +/* Algorithm calculates 0x200 - 0x10 log2 (input) */ +u8 sumVolumeToAttenuation(u32 value) +{ + u16 count = 16; + s16 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 = (s16) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (u8) ans; +} + +/******************************************* +* write/read PCI function 0 registers * +********************************************/ +void sblive_writefn0(struct emu10k1_card *card, u8 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + + return; +} + +void sblive_wrtmskfn0(struct emu10k1_card *card, u8 reg, u32 mask, u32 data) +{ + unsigned long flags; + + data &= mask; + + spin_lock_irqsave(&card->lock, flags); + data |= inl(card->iobase + reg) & ~mask; + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + + return; +} + +u32 sblive_readfn0(struct emu10k1_card * card, u8 reg) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + return val; +} + +u32 sblive_rdmskfn0(struct emu10k1_card * card, u8 reg, u32 mask) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + return val & mask; +} + +/************************************************************************ +* write/read Emu10k1 pointer-offset register set, accessed through * +* the PTR and DATA registers * +*************************************************************************/ +void sblive_writeptr(struct emu10k1_card *card, u32 reg, u32 channel, u32 data) +{ + u32 regptr; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + data |= inl(card->iobase + DATA) & ~mask; + outl(data, card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + } else { + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + outl(data, card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + } + + return; +} + +u32 sblive_readptr(struct emu10k1_card * card, u32 reg, u32 channel) +{ + u32 regptr, val; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + val = inl(card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&card->lock, flags); + outl(regptr, card->iobase + PTR); + val = inl(card->iobase + DATA); + spin_unlock_irqrestore(&card->lock, flags); + + return val; + } +} + +void emu10k1_set_stop_on_loop(struct emu10k1_card *card, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 1); + else + sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 1); + + return; +} + +void emu10k1_clear_stop_on_loop(struct emu10k1_card *card, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0); + else + sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 0); + + return; +} + +static void sblive_wcwait(struct emu10k1_card *card, u32 wait) +{ + volatile unsigned uCount; + u32 newtime = 0, curtime; + + curtime = READ_FN0(card, WC_SAMPLECOUNTER); + while (wait--) { + uCount = 0; + while (uCount++ < TIMEOUT) { + newtime = READ_FN0(card, WC_SAMPLECOUNTER); + if (newtime != curtime) + break; + } + + if (uCount >= TIMEOUT) + break; + + curtime = newtime; + } +} + +int sblive_readac97(struct emu10k1_card *card, u8 index, u16 * data) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(index, card->mixeraddx + 2); + *data = inw(card->mixeraddx); + + spin_unlock_irqrestore(&card->lock, flags); + + return CTSTATUS_SUCCESS; +} + +int sblive_writeac97(struct emu10k1_card *card, u8 index, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(index, card->mixeraddx + 2); + outw(data, card->mixeraddx); + + spin_unlock_irqrestore(&card->lock, flags); + + return CTSTATUS_SUCCESS; +} + +int sblive_rmwac97(struct emu10k1_card *card, u8 index, u16 data, u16 mask) +{ + u16 temp; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(index, card->mixeraddx + 2); + temp = inw(card->mixeraddx); + temp &= ~mask; + data &= mask; + temp |= data; + outw(temp, card->mixeraddx); + + spin_unlock_irqrestore(&card->lock, flags); + + return CTSTATUS_SUCCESS; +} + +/********************************************************* +* MPU access functions * +**********************************************************/ + +int emu10k1_mpu_write_data(struct emu10k1_card *card, u8 data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&card->lock, flags); + + if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) { + outb(data, card->iobase + MUDATA); + ret = CTSTATUS_SUCCESS; + } else + ret = CTSTATUS_BUSY; + + spin_unlock_irqrestore(&card->lock, flags); + + return ret; +} + +int emu10k1_mpu_read_data(struct emu10k1_card *card, u8 * data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&card->lock, flags); + + if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) { + *data = inb(card->iobase + MUDATA); + ret = CTSTATUS_SUCCESS; + } else + ret = CTSTATUS_NODATA; + + spin_unlock_irqrestore(&card->lock, flags); + + return ret; +} + +int emu10k1_mpu_reset(struct emu10k1_card *card) +{ + u8 status; + unsigned long flags; + + DPF(2, "emu10k1_mpu_reset()\n"); + + if (card->mpuacqcount == 0) { + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_RESET, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_RESET, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + outb(MUCMD_ENTERUARTMODE, card->iobase + MUCMD); + spin_unlock_irqrestore(&card->lock, flags); + + sblive_wcwait(card, 8); + + spin_lock_irqsave(&card->lock, flags); + status = inb(card->iobase + MUDATA); + spin_unlock_irqrestore(&card->lock, flags); + + if (status == 0xfe) + return CTSTATUS_SUCCESS; + else + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} + +int emu10k1_mpu_acquire(struct emu10k1_card *card) +{ + /* FIXME: This should be a macro */ + ++card->mpuacqcount; + + return CTSTATUS_SUCCESS; +} + +int emu10k1_mpu_release(struct emu10k1_card *card) +{ + /* FIXME: this should be a macro */ + --card->mpuacqcount; + + return CTSTATUS_SUCCESS; +} diff --git a/drivers/sound/emu10k1/hwaccess.h b/drivers/sound/emu10k1/hwaccess.h new file mode 100644 index 000000000..0dba188d3 --- /dev/null +++ b/drivers/sound/emu10k1/hwaccess.h @@ -0,0 +1,204 @@ +/* + ********************************************************************** + * hwaccess.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _HWACCESS_H +#define _HWACCESS_H + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/ioport.h> +#include <linux/sound.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <linux/interrupt.h> + +#include <asm/io.h> +#include <asm/dma.h> + +#include <emu_wrapper.h> + +enum GlobalErrorCode +{ + CTSTATUS_SUCCESS = 0x0000, + CTSTATUS_ERROR, + CTSTATUS_NOMEMORY, + CTSTATUS_INUSE, +}; + +#define FLAGS_AVAILABLE 0x0001 +#define FLAGS_READY 0x0002 + +#define min(x,y) ((x) < (y)) ? (x) : (y) + +struct memhandle +{ + unsigned long busaddx; + void *virtaddx; + u32 order; +}; + +struct memhandle *emu10k1_alloc_memphysical(u32); +void emu10k1_free_memphysical(struct memhandle *); + +#define DEBUG_LEVEL 2 + +#ifdef EMU10K1_DEBUG +# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0) +# define DPF(level,x) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0) +#define ERROR() DPF(1,"error\n"); +#else +# define DPD(level,x,y...) /* not debugging: nothing */ +# define DPF(level,x) +#define ERROR() +#endif /* EMU10K1_DEBUG */ + +#include "8010.h" +#include "voicemgr.h" + +int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *); +void emu10k1_addxmgr_free(struct emu10k1_card *, int); + +#include "timer.h" +#include "irqmgr.h" + +/* DATA STRUCTURES */ + +struct emu10k1_card +{ + struct list_head list; + + struct memhandle *virtualpagetable; + + struct memhandle *tankmem; + u32 tmemsize; + struct memhandle *silentpage; + + spinlock_t lock; + + struct voice_manager voicemgr; + u16 emupagetable[MAXPAGES]; + + struct list_head timers; + unsigned timer_delay; + spinlock_t timer_lock; + + struct pci_dev *pci_dev; + unsigned long iobase; + unsigned long mixeraddx; + u32 irq; + + unsigned long audio1_num; + unsigned long audio2_num; + unsigned long mixer_num; + unsigned long midi_num; + + struct emu10k1_waveout *waveout; + struct emu10k1_wavein *wavein; + struct emu10k1_mpuout *mpuout; + struct emu10k1_mpuin *mpuin; + + u16 arrwVol[SOUND_MIXER_NRDEVICES + 1]; + /* array is used from the member 1 to save (-1) operation */ + u32 digmix[96]; + unsigned int modcnt; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + u32 mpuacqcount; // Mpu acquire count + u32 has_toslink; // TOSLink detection + + u8 chiprev; /* Chip revision */ +}; + +#ifdef PRIVATE_PCM_VOLUME + +#define MAX_PCM_CHANNELS NUM_G +struct sblive_pcm_volume_rec { + struct files_struct *files; // identification of the same thread + u8 attn_l; // attenuation for left channel + u8 attn_r; // attenuation for right channel + u16 mixer; // saved mixer value for return + u8 channel_l; // idx of left channel + u8 channel_r; // idx of right channel + int opened; // counter - locks element +}; +extern struct sblive_pcm_volume_rec sblive_pcm_volume[]; + +#endif + + +#define ENABLE 0xffffffff +#define DISABLE 0x00000000 + +#define ENV_ON 0x80 +#define ENV_OFF 0x00 + +#define TIMEOUT 16384 + +u32 srToPitch(u32); +u8 sumVolumeToAttenuation(u32); + +extern struct list_head emu10k1_devs; + +/* Hardware Abstraction Layer access functions */ + +#define WRITE_FN0(a,b,c) sblive_wrtmskfn0((a),(u8)(b), ((1 << (((b) >> 24) & 0x3f)) - 1) << (((b) >> 16) & 0x1f), (c) << (((b) >> 16) & 0x1f)) + +#define READ_FN0(a,b) sblive_rdmskfn0((a),(u8)(b),((1 << (((b) >> 24) & 0x3f)) - 1) << (((b) >> 16) & 0x1f)) >> (((b) >> 16) & 0x1f) + +void sblive_writefn0(struct emu10k1_card *, u8 , u32 ); +void sblive_wrtmskfn0(struct emu10k1_card *, u8 , u32 , u32 ); + +u32 sblive_readfn0(struct emu10k1_card *, u8 ); +u32 sblive_rdmskfn0(struct emu10k1_card *, u8, u32 ); + +void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 ); +u32 sblive_readptr(struct emu10k1_card *, u32 , u32 ); + +void emu10k1_set_stop_on_loop(struct emu10k1_card *, u32); +void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32); + +/* AC97 Mixer access function */ +int sblive_readac97(struct emu10k1_card *, u8, u16 *); +int sblive_writeac97(struct emu10k1_card *, u8, u16); +int sblive_rmwac97(struct emu10k1_card *, u8, u16, u16); + +/* MPU access function*/ +int emu10k1_mpu_write_data(struct emu10k1_card *, u8); +int emu10k1_mpu_read_data(struct emu10k1_card *, u8 *); +int emu10k1_mpu_reset(struct emu10k1_card *); +int emu10k1_mpu_acquire(struct emu10k1_card *); +int emu10k1_mpu_release(struct emu10k1_card *); + +#endif /* _HWACCESS_H */ diff --git a/drivers/sound/emu10k1/icardmid.h b/drivers/sound/emu10k1/icardmid.h new file mode 100644 index 000000000..6a6ef4194 --- /dev/null +++ b/drivers/sound/emu10k1/icardmid.h @@ -0,0 +1,163 @@ +/* + ********************************************************************** + * isblive_mid.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDMIDI_H +#define _ICARDMIDI_H + +/* MIDI defines */ +#define MIDI_DATA_FIRST 0x00 +#define MIDI_DATA_LAST 0x7F +#define MIDI_STATUS_FIRST 0x80 +#define MIDI_STATUS_LAST 0xFF + +/* Channel status bytes */ +#define MIDI_STATUS_CHANNEL_FIRST 0x80 +#define MIDI_STATUS_CHANNEL_LAST 0xE0 +#define MIDI_STATUS_CHANNEL_MASK 0xF0 + +/* Channel voice messages */ +#define MIDI_VOICE_NOTE_OFF 0x80 +#define MIDI_VOICE_NOTE_ON 0x90 +#define MIDI_VOICE_POLY_PRESSURE 0xA0 +#define MIDI_VOICE_CONTROL_CHANGE 0xB0 +#define MIDI_VOICE_PROGRAM_CHANGE 0xC0 +#define MIDI_VOICE_CHANNEL_PRESSURE 0xD0 +#define MIDI_VOICE_PITCH_BEND 0xE0 + +/* Channel mode messages */ +#define MIDI_MODE_CHANNEL MIDI_VOICE_CONTROL_CHANGE + +/* System status bytes */ +#define MIDI_STATUS_SYSTEM_FIRST 0xF0 +#define MIDI_STATUS_SYSTEM_LAST 0xFF + +/* System exclusive messages */ +#define MIDI_SYSEX_BEGIN 0xF0 +#define MIDI_SYSEX_EOX 0xF7 + +/* System common messages */ +#define MIDI_COMMON_TCQF 0xF1 /* Time code quarter frame */ +#define MIDI_COMMON_SONG_POSITION 0xF2 +#define MIDI_COMMON_SONG_SELECT 0xF3 +#define MIDI_COMMON_UNDEFINED_F4 0xF4 +#define MIDI_COMMON_UNDEFINED_F5 0xF5 +#define MIDI_COMMON_TUNE_REQUEST 0xF6 + +/* System real-time messages */ +#define MIDI_RTIME_TIMING_CLOCK 0xF8 +#define MIDI_RTIME_UNDEFINED_F9 0xF9 +#define MIDI_RTIME_START 0xFA +#define MIDI_RTIME_CONTINUE 0xFB +#define MIDI_RTIME_STOP 0xFC +#define MIDI_RTIME_UNDEFINED_FD 0xFD +#define MIDI_RTIME_ACTIVE_SENSING 0xFE +#define MIDI_RTIME_SYSTEM_RESET 0xFF + +/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */ +#define MIDI_CACHE_ALL 1 +#define MIDI_CACHE_BESTFIT 2 +#define MIDI_CACHE_QUERY 3 +#define MIDI_UNCACHE 4 + +/* Event declarations for MPU IRQ Callbacks */ +#define ICARDMIDI_INLONGDATA 0x00000001 /* MIM_LONGDATA */ +#define ICARDMIDI_INLONGERROR 0x00000002 /* MIM_LONGERROR */ +#define ICARDMIDI_OUTLONGDATA 0x00000004 /* MOM_DONE for MPU OUT buffer */ +#define ICARDMIDI_INDATA 0x00000010 /* MIM_DATA */ +#define ICARDMIDI_INDATAERROR 0x00000020 /* MIM_ERROR */ + +/* Declaration for flags in CARDMIDIBUFFERHDR */ +/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */ +#define MIDIBUF_DONE 0x00000001 +#define MIDIBUF_INQUEUE 0x00000004 + +/* Declaration for msg parameter in midiCallbackFn */ +#define ICARDMIDI_OUTBUFFEROK 0x00000001 +#define ICARDMIDI_INMIDIOK 0x00000002 + +/* Declaration for technology in struct midi_caps */ +#define MT_MIDIPORT 0x00000001 /* In original MIDIOUTCAPS structure */ +#define MT_FMSYNTH 0x00000004 /* In original MIDIOUTCAPS structure */ +#define MT_AWESYNTH 0x00001000 +#define MT_PCISYNTH 0x00002000 +#define MT_PCISYNTH64 0x00004000 +#define CARDMIDI_AWEMASK 0x0000F000 + +enum LocalErrorCode +{ + CTSTATUS_NOTENABLED = 0x7000, + CTSTATUS_READY, + CTSTATUS_BUSY, + CTSTATUS_DATAAVAIL, + CTSTATUS_NODATA, + CTSTATUS_NEXT_BYTE +}; + +/* MIDI data block header */ +struct midi_hdr +{ + u8 *reserved; /* Pointer to original locked data block */ + u32 bufferlength; /* Length of data in data block */ + u32 bytesrecorded; /* Used for input only */ + u32 user; /* For client's use */ + u32 flags; /* Assorted flags (see defines) */ + struct list_head list; /* Reserved for driver */ + u8 *data; /* Second copy of first pointer */ +}; + +/* Enumeration for SetControl */ +enum +{ + MIDIOBJVOLUME = 0x1, + MIDIQUERYACTIVEINST +}; + +struct midi_queue +{ + struct midi_queue *next; + u32 qtype; /* 0 = short message, 1 = long data */ + u32 length; + u32 sizeLeft; + u8 *midibyte; + unsigned long refdata; +}; + +struct midi_openinfo +{ + u32 cbsize; + u32 flags; + unsigned long refdata; + u32 streamid; +}; + +int emu10k1_midi_callback(unsigned long , unsigned long, unsigned long *); + +#endif /* _ICARDMIDI_H */ diff --git a/drivers/sound/emu10k1/icardwav.h b/drivers/sound/emu10k1/icardwav.h new file mode 100644 index 000000000..a141e7396 --- /dev/null +++ b/drivers/sound/emu10k1/icardwav.h @@ -0,0 +1,52 @@ +/* + ********************************************************************** + * icardwav.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDWAV_H +#define _ICARDWAV_H + +/* Enumeration for SetControl */ +enum +{ + WAVECURPOS = 0x10, +}; + +struct wave_format +{ + u32 samplingrate; + u32 bitsperchannel; + u32 channels; /* 1 = Mono, 2 = Stereo */ +}; + +/* emu10k1_wave states */ +#define CARDWAVE_STATE_STOPPED 0x0001 +#define CARDWAVE_STATE_STARTED 0x0002 + +#endif /* _ICARDWAV_H */ diff --git a/drivers/sound/emu10k1/irqmgr.c b/drivers/sound/emu10k1/irqmgr.c new file mode 100644 index 000000000..96bffcfe4 --- /dev/null +++ b/drivers/sound/emu10k1/irqmgr.c @@ -0,0 +1,127 @@ + +/* + ********************************************************************** + * irqmgr.c - IRQ manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmi.h" +#include "cardmo.h" +#include "irqmgr.h" + +/* Interrupt handler */ + +void emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct emu10k1_card *card = (struct emu10k1_card *) dev_id; + u32 irqstatus, ptr, tmp; + + if (!(irqstatus = sblive_readfn0(card, IPR))) + return; + + DPD(4, "emu10k1_interrupt called, irq = %u\n", irq); + + /* Preserve PTR register */ + ptr = sblive_readfn0(card, PTR); + + /* + ** NOTE : + ** We do a 'while loop' here cos on certain machines, with both + ** playback and recording going on at the same time, IRQs will + ** stop coming in after a while. Checking IPND indeed shows that + ** there are interrupts pending but the PIC says no IRQs pending. + ** I suspect that some boards need edge-triggered IRQs but are not + ** getting that condition if we don't completely clear the IPND + ** (make sure no more interrupts are pending). + ** - Eric + */ + + do { + DPD(4, "irq status %x\n", irqstatus); + + tmp = irqstatus; + + if (irqstatus & IRQTYPE_TIMER) { + emu10k1_timer_irqhandler(card); + irqstatus &= ~IRQTYPE_TIMER; + } + + if (irqstatus & IRQTYPE_MPUIN) { + emu10k1_mpuin_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUIN; + } + + if (irqstatus & IRQTYPE_MPUOUT) { + emu10k1_mpuout_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUOUT; + } + + if (irqstatus) + emu10k1_irq_disable(card, irqstatus); + + sblive_writefn0(card, IPR, tmp); + + } while ((irqstatus = sblive_readfn0(card, IPR))); + + sblive_writefn0(card, PTR, ptr); + + return; +} + +/* Enables the specified irq service */ + +int emu10k1_irq_enable(struct emu10k1_card *card, u32 irqtype) +{ + /* + * TODO : + * put protection here so that we don't accidentally + * screw-up another cardxxx objects irqs + */ + + DPD(4, "emu10k1_irq_enable %x\n", irqtype); + sblive_wrtmskfn0(card, INTE, irqtype, ENABLE); + + return CTSTATUS_SUCCESS; +} + +/* Disables the specified irq service */ + +int emu10k1_irq_disable(struct emu10k1_card *card, u32 irqtype) +{ + /* + * TODO : + * put protection here so that we don't accidentally + * screw-up another cardxxx objects irqs + */ + + DPD(4, "emu10k1_irq_disable %x\n", irqtype); + sblive_wrtmskfn0(card, INTE, irqtype, DISABLE); + + return CTSTATUS_SUCCESS; +} diff --git a/drivers/sound/emu10k1/irqmgr.h b/drivers/sound/emu10k1/irqmgr.h new file mode 100644 index 000000000..886a7374e --- /dev/null +++ b/drivers/sound/emu10k1/irqmgr.h @@ -0,0 +1,59 @@ +/* + ********************************************************************** + * irq.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _IRQ_H +#define _IRQ_H + +/* EMU Irq Types */ +#define IRQTYPE_PCIBUSERROR IPR_PCIERROR +#define IRQTYPE_MIXERBUTTON (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE) +#define IRQTYPE_VOICE (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK) +#define IRQTYPE_RECORD (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL) +#define IRQTYPE_MPUOUT IPR_MIDITRANSBUFEMPTY +#define IRQTYPE_MPUIN IPR_MIDIRECVBUFEMPTY +#define IRQTYPE_TIMER IPR_INTERVALTIMER +#define IRQTYPE_SPDIF (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE) +#define IRQTYPE_DSP IPR_FXDSP + +struct emu10k1_wavedevice +{ + struct emu10k1_card *card; + struct wiinst *wiinst; + struct woinst *woinst; + u16 enablebits; +}; + +void emu10k1_timer_irqhandler(struct emu10k1_card *); + +int emu10k1_irq_enable(struct emu10k1_card *, u32); +int emu10k1_irq_disable(struct emu10k1_card *, u32); + +#endif /* _IRQ_H */ diff --git a/drivers/sound/emu10k1/main.c b/drivers/sound/emu10k1/main.c new file mode 100644 index 000000000..bbdf721fb --- /dev/null +++ b/drivers/sound/emu10k1/main.c @@ -0,0 +1,825 @@ + +/* + ********************************************************************** + * main.c - Creative EMU10K1 audio driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + ********************************************************************** + * + * Supported devices: + * /dev/dsp: Standard /dev/dsp device, OSS-compatible + * /dev/mixer: Standard /dev/mixer device, OSS-compatible + * /dev/midi: Raw MIDI UART device, mostly OSS-compatible + * + * Revision history: + * 0.1 beta Initial release + * 0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support. + * 0.3 Fixed mixer routing bug, added APS, joystick support. + * 0.4 Added rear-channel, SPDIF support. + * 0.5 Source cleanup, SMP fixes, multiopen support, 64 bit arch fixes, + * moved bh's to tasklets, moved to the new PCI driver initialization style. + ********************************************************************** + */ + +/* These are only included once per module */ +#include <linux/module.h> +#include <linux/init.h> + +#include "hwaccess.h" +#include "efxmgr.h" +#include "cardwo.h" +#include "cardwi.h" +#include "cardmo.h" +#include "cardmi.h" +#include "recmgr.h" + +#define DRIVER_VERSION "0.5" + +/* FIXME: is this right? */ +#define EMU10K1_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ + +#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 + +#define EMU10K1_EXTENT 0x20 /* 32 byte I/O space */ + +enum { + EMU10K1 = 0, +}; + +static char *card_names[] __devinitdata = { + "EMU10K1", +}; + +static struct pci_device_id emu10k1_pci_tbl[] __initdata = { + {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, emu10k1_pci_tbl); + +/* Global var instantiation */ + +LIST_HEAD(emu10k1_devs); + +extern struct file_operations emu10k1_audio_fops; +extern struct file_operations emu10k1_mixer_fops; +extern struct file_operations emu10k1_midi_fops; + +extern void emu10k1_interrupt(int, void *, struct pt_regs *s); +extern int emu10k1_mixer_wrch(struct emu10k1_card *, unsigned int, int); + +static int __devinit audio_init(struct emu10k1_card *card) +{ + if ((card->waveout = kmalloc(sizeof(struct emu10k1_waveout), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_waveout: out of memory\n"); + return CTSTATUS_ERROR; + } + memset(card->waveout, 0, sizeof(struct emu10k1_waveout)); + + /* Assign default global volume, reverb, chorus */ + card->waveout->globalvol = 0xffffffff; + card->waveout->left = 0xffff; + card->waveout->right = 0xffff; + card->waveout->mute = 0; + card->waveout->globalreverb = 0xffffffff; + card->waveout->globalchorus = 0xffffffff; + + if ((card->wavein = kmalloc(sizeof(struct emu10k1_wavein), GFP_KERNEL)) + == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_wavein: out of memory\n"); + return CTSTATUS_ERROR; + } + memset(card->wavein, 0, sizeof(struct emu10k1_wavein)); + + card->wavein->recsrc = WAVERECORD_AC97; + + return CTSTATUS_SUCCESS; +} + +static void __devinit mixer_init(struct emu10k1_card *card) +{ + int count; + struct initvol { + int mixch; + int vol; + } initvol[] = { + { + SOUND_MIXER_VOLUME, 0x5050}, { + SOUND_MIXER_OGAIN, 0x3232}, { + SOUND_MIXER_SPEAKER, 0x3232}, { + SOUND_MIXER_PHONEIN, 0x3232}, { + SOUND_MIXER_MIC, 0x0000}, { + SOUND_MIXER_LINE, 0x0000}, { + SOUND_MIXER_CD, 0x3232}, { + SOUND_MIXER_LINE1, 0x3232}, { + SOUND_MIXER_LINE3, 0x3232}, { + SOUND_MIXER_DIGITAL1, 0x6464}, { + SOUND_MIXER_DIGITAL2, 0x6464}, { + SOUND_MIXER_PCM, 0x6464}, { + SOUND_MIXER_RECLEV, 0x3232}, { + SOUND_MIXER_TREBLE, 0x3232}, { + SOUND_MIXER_BASS, 0x3232}, { + SOUND_MIXER_LINE2, 0x4b4b}}; + + int initdig[] = { 0, 1, 2, 3, 6, 7, 16, 17, 18, 19, 22, 23, 64, 65, 66, 67, 70, 71, + 84, 85 + }; + + /* Reset */ + sblive_writeac97(card, AC97_RESET, 0); + +#if 0 + /* Check status word */ + { + u16 reg; + + sblive_readac97(card, AC97_RESET, ®); + DPD(2, "RESET 0x%x\n", reg); + sblive_readac97(card, AC97_MASTERTONE, ®); + DPD(2, "MASTER_TONE 0x%x\n", reg); + } +#endif + + /* Set default recording source to mic in */ + sblive_writeac97(card, AC97_RECORDSELECT, 0); + + /* Set default AC97 "PCM" volume to acceptable max */ + //sblive_writeac97(card, AC97_PCMOUTVOLUME, 0); + //sblive_writeac97(card, AC97_LINE2, 0); + + /* Set default volumes for all mixer channels */ + + for (count = 0; count < sizeof(card->digmix) / sizeof(card->digmix[0]); count++) { + card->digmix[count] = 0x80000000; + sblive_writeptr(card, FXGPREGBASE + 0x10 + count, 0, 0); + } + + for (count = 0; count < sizeof(initdig) / sizeof(initdig[0]); count++) { + card->digmix[initdig[count]] = 0x7fffffff; + sblive_writeptr(card, FXGPREGBASE + 0x10 + initdig[count], 0, 0x7fffffff); + } + + for (count = 0; count < sizeof(initvol) / sizeof(initvol[0]); count++) { + emu10k1_mixer_wrch(card, initvol[count].mixch, initvol[count].vol); + } + + card->modcnt = 0; // Should this be here or in open() ? + + return; +} + +static int __devinit midi_init(struct emu10k1_card *card) +{ + if ((card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL)) + == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n"); + return CTSTATUS_ERROR; + } + + memset(card->mpuout, 0, sizeof(struct emu10k1_mpuout)); + + card->mpuout->intr = 1; + card->mpuout->status = FLAGS_AVAILABLE; + card->mpuout->state = CARDMIDIOUT_STATE_DEFAULT; + + tasklet_init(&card->mpuout->tasklet, emu10k1_mpuout_bh, (unsigned long) card); + + spin_lock_init(&card->mpuout->lock); + + if ((card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL)) == NULL) { + kfree(card->mpuout); + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n"); + return CTSTATUS_ERROR; + } + + memset(card->mpuin, 0, sizeof(struct emu10k1_mpuin)); + + card->mpuin->status = FLAGS_AVAILABLE; + + tasklet_init(&card->mpuin->tasklet, emu10k1_mpuin_bh, (unsigned long) card->mpuin); + + spin_lock_init(&card->mpuin->lock); + + /* Reset the MPU port */ + if (emu10k1_mpu_reset(card) != CTSTATUS_SUCCESS) { + ERROR(); + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} + +static void __devinit voice_init(struct emu10k1_card *card) +{ + struct voice_manager *voicemgr = &card->voicemgr; + struct emu_voice *voice; + int i; + + voicemgr->card = card; + voicemgr->lock = SPIN_LOCK_UNLOCKED; + + voice = voicemgr->voice; + for (i = 0; i < NUM_G; i++) { + voice->card = card; + voice->usage = VOICEMGR_USAGE_FREE; + voice->num = i; + voice->linked_voice = NULL; + voice++; + } + + return; +} + +static void __devinit timer_init(struct emu10k1_card *card) +{ + INIT_LIST_HEAD(&card->timers); + card->timer_delay = TIMER_STOPPED; + card->timer_lock = SPIN_LOCK_UNLOCKED; + + return; +} + +static void __devinit addxmgr_init(struct emu10k1_card *card) +{ + u32 count; + + for (count = 0; count < MAXPAGES; count++) + card->emupagetable[count] = 0; + + /* Mark first page as used */ + /* This page is reserved by the driver */ + card->emupagetable[0] = 0x8001; + card->emupagetable[1] = MAXPAGES - RESERVED - 1; + + return; +} + +static void __devinit fx_init(struct emu10k1_card *card) +{ + int i, j, k, l; + u32 pc = 0; + + for (i = 0; i < 512; i++) + OP(6, 0x40, 0x40, 0x40, 0x40); + + for (i = 0; i < 256; i++) + sblive_writeptr(card, FXGPREGBASE + i, 0, 0); + + pc = 0; + + for (j = 0; j < 2; j++) { + + OP(4, 0x100, 0x40, j, 0x44); + OP(4, 0x101, 0x40, j + 2, 0x44); + + for (i = 0; i < 6; i++) { + k = i * 16 + j; + OP(0, 0x102, 0x40, 0x110 + k, 0x100); + OP(0, 0x102, 0x102, 0x112 + k, 0x101); + OP(0, 0x102, 0x102, 0x114 + k, 0x10 + j); + OP(0, 0x102, 0x102, 0x116 + k, 0x12 + j); + OP(0, 0x102, 0x102, 0x118 + k, 0x14 + j); + OP(0, 0x102, 0x102, 0x11a + k, 0x16 + j); + OP(0, 0x102, 0x102, 0x11c + k, 0x18 + j); + OP(0, 0x102, 0x102, 0x11e + k, 0x1a + j); + + k = 0x190 + i * 8 + j * 4; + OP(0, 0x40, 0x40, 0x102, 0x170 + j); + OP(7, k + 1, k, k + 1, 0x174 + j); + OP(7, k, 0x102, k, 0x172 + j); + OP(7, k + 3, k + 2, k + 3, 0x178 + j); + OP(0, k + 2, 0x56, k + 2, 0x176 + j); + OP(6, k + 2, k + 2, k + 2, 0x40); + + l = 0x1c0 + i * 8 + j * 4; + OP(0, 0x40, 0x40, k + 2, 0x180 + j); + OP(7, l + 1, l, l + 1, 0x184 + j); + OP(7, l, k + 2, l, 0x182 + j); + OP(7, l + 3, l + 2, l + 3, 0x188 + j); + OP(0, l + 2, 0x56, l + 2, 0x186 + j); + OP(4, l + 2, 0x40, l + 2, 0x46); + + OP(6, 0x20 + (i * 2) + j, l + 2, 0x40, 0x40); + + } + } + sblive_writeptr(card, DBG, 0, 0); + + return; +} + +static int __devinit hw_init(struct emu10k1_card *card) +{ + int nCh; + +#ifdef TANKMEM + u32 size = 0; +#endif + u32 sizeIdx = 0; + u32 pagecount, tmp; + + /* Disable audio and lock cache */ + sblive_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE); + + /* Reset recording buffers */ + sblive_writeptr(card, MICBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, MICBA, 0, 0); + sblive_writeptr(card, FXBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, FXBA, 0, 0); + sblive_writeptr(card, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, ADCBA, 0, 0); + + /* Disable channel interrupt */ + sblive_writefn0(card, INTE, DISABLE); + sblive_writeptr(card, CLIEL, 0, 0); + sblive_writeptr(card, CLIEH, 0, 0); + sblive_writeptr(card, SOLEL, 0, 0); + sblive_writeptr(card, SOLEH, 0, 0); + + /* Init envelope engine */ + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr(card, DCYSUSV, nCh, ENV_OFF); + sblive_writeptr(card, IP, nCh, 0); + sblive_writeptr(card, VTFT, nCh, 0xffff); + sblive_writeptr(card, CVCF, nCh, 0xffff); + sblive_writeptr(card, PTRX, nCh, 0); + sblive_writeptr(card, CPF, nCh, 0); + sblive_writeptr(card, CCR, nCh, 0); + + sblive_writeptr(card, PSST, nCh, 0); + sblive_writeptr(card, DSL, nCh, 0x10); + sblive_writeptr(card, CCCA, nCh, 0); + sblive_writeptr(card, Z1, nCh, 0); + sblive_writeptr(card, Z2, nCh, 0); + sblive_writeptr(card, FXRT, nCh, 0xd01c0000); + + sblive_writeptr(card, ATKHLDM, nCh, 0); + sblive_writeptr(card, DCYSUSM, nCh, 0); + sblive_writeptr(card, IFATN, nCh, 0xffff); + sblive_writeptr(card, PEFE, nCh, 0); + sblive_writeptr(card, FMMOD, nCh, 0); + sblive_writeptr(card, TREMFRQ, nCh, 24); /* 1 Hz */ + sblive_writeptr(card, FM2FRQ2, nCh, 24); /* 1 Hz */ + sblive_writeptr(card, TEMPENV, nCh, 0); + + /*** These are last so OFF prevents writing ***/ + sblive_writeptr(card, LFOVAL2, nCh, 0); + sblive_writeptr(card, LFOVAL1, nCh, 0); + sblive_writeptr(card, ATKHLDV, nCh, 0); + sblive_writeptr(card, ENVVOL, nCh, 0); + sblive_writeptr(card, ENVVAL, nCh, 0); + } + + /* + ** 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 (Digital audio) + ** P = 0 (Consumer) + */ + + /* SPDIF0 */ + sblive_writeptr(card, SPCS0, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + /* SPDIF1 */ + sblive_writeptr(card, SPCS1, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + /* SPDIF2 & SPDIF3 */ + sblive_writeptr(card, SPCS2, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + fx_init(card); /* initialize effects engine */ + + card->tankmem = NULL; + +#ifdef TANKMEM + size = TMEMSIZE; + sizeIdx = TMEMSIZEREG; + while (size > 16384) { + if ((card->tankmem = emu10k1_alloc_memphysical(size)) != NULL) + break; + + size /= 2; + sizeIdx -= 1; + } + + if (card->tankmem == NULL) { + card->tmemsize = 0; + return CTSTATUS_ERROR; + } + + card->tmemsize = size; +#else /* !TANKMEM */ + card->tmemsize = 0; +#endif /* TANKMEM */ + + if ((card->virtualpagetable = emu10k1_alloc_memphysical((MAXPAGES - RESERVED) * sizeof(u32))) == NULL) { + ERROR(); + emu10k1_free_memphysical(card->tankmem); + return CTSTATUS_ERROR; + } + + if ((card->silentpage = emu10k1_alloc_memphysical(EMUPAGESIZE)) == NULL) { + ERROR(); + emu10k1_free_memphysical(card->tankmem); + emu10k1_free_memphysical(card->virtualpagetable); + return CTSTATUS_ERROR; + } else + memset(card->silentpage->virtaddx, 0, EMUPAGESIZE); + + for (pagecount = 0; pagecount < (MAXPAGES - RESERVED); pagecount++) + + ((u32 *) card->virtualpagetable->virtaddx)[pagecount] = (card->silentpage->busaddx * 2) | pagecount; + + /* Init page table & tank memory base register */ + sblive_writeptr(card, PTB, 0, card->virtualpagetable->busaddx); +#ifdef TANKMEM + sblive_writeptr(card, TCB, 0, card->tankmem->busaddx); +#else + sblive_writeptr(card, TCB, 0, 0); +#endif + sblive_writeptr(card, TCBS, 0, sizeIdx); + + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr(card, MAPA, nCh, MAP_PTI_MASK | (card->silentpage->busaddx * 2)); + sblive_writeptr(card, MAPB, nCh, MAP_PTI_MASK | (card->silentpage->busaddx * 2)); + } + + /* Hokay, now enable the AUD bit */ + /* Enable Audio = 1 */ + /* Mute Disable Audio = 0 */ + /* Lock Tank Memory = 1 */ + /* Lock Sound Memory = 0 */ + /* Auto Mute = 1 */ + + sblive_rmwac97(card, AC97_MASTERVOLUME, 0x8000, 0x8000); + + sblive_writeac97(card, AC97_MASTERVOLUME, 0); + sblive_writeac97(card, AC97_PCMOUTVOLUME, 0); + + sblive_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE | HCFG_AUTOMUTE | HCFG_JOYENABLE); + + /* TOSLink detection */ + card->has_toslink = 0; + + tmp = sblive_readfn0(card, HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + sblive_writefn0(card, HCFG, tmp | 0x800); + + udelay(512); + + if (tmp != (sblive_readfn0(card, HCFG) & ~0x800)) { + card->has_toslink = 1; + sblive_writefn0(card, HCFG, tmp); + } + } + + return CTSTATUS_SUCCESS; +} + +static int __devinit emu10k1_init(struct emu10k1_card *card) +{ + /* Init Card */ + if (hw_init(card) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + voice_init(card); + timer_init(card); + addxmgr_init(card); + + DPD(2, " hw control register -> %x\n", sblive_readfn0(card, HCFG)); + + return CTSTATUS_SUCCESS; +} + +static void __devexit audio_exit(struct emu10k1_card *card) +{ + kfree(card->waveout); + kfree(card->wavein); + return; +} + +static void __devexit midi_exit(struct emu10k1_card *card) +{ + tasklet_unlock_wait(&card->mpuout->tasklet); + kfree(card->mpuout); + + tasklet_unlock_wait(&card->mpuin->tasklet); + kfree(card->mpuin); + + return; +} + +static void __devexit emu10k1_exit(struct emu10k1_card *card) +{ + int ch; + + sblive_writefn0(card, INTE, DISABLE); + + /** Shutdown the chip **/ + for (ch = 0; ch < NUM_G; ch++) + sblive_writeptr(card, DCYSUSV, ch, ENV_OFF); + + for (ch = 0; ch < NUM_G; ch++) { + sblive_writeptr(card, VTFT, ch, 0); + sblive_writeptr(card, CVCF, ch, 0); + sblive_writeptr(card, PTRX, ch, 0); + sblive_writeptr(card, CPF, ch, 0); + } + + /* Reset recording buffers */ + sblive_writeptr(card, MICBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, MICBA, 0, 0); + sblive_writeptr(card, FXBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, FXBA, 0, 0); + sblive_writeptr(card, FXWC, 0, 0); + sblive_writeptr(card, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(card, ADCBA, 0, 0); + sblive_writeptr(card, TCBS, 0, TCBS_BUFFSIZE_16K); + sblive_writeptr(card, TCB, 0, 0); + sblive_writeptr(card, DBG, 0, 0x8000); + + /* Disable channel interrupt */ + sblive_writeptr(card, CLIEL, 0, 0); + sblive_writeptr(card, CLIEH, 0, 0); + sblive_writeptr(card, SOLEL, 0, 0); + sblive_writeptr(card, SOLEH, 0, 0); + + /* Disable audio and lock cache */ + sblive_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE); + sblive_writeptr(card, PTB, 0, 0); + + emu10k1_free_memphysical(card->silentpage); + emu10k1_free_memphysical(card->virtualpagetable); +#ifdef TANKMEM + emu10k1_free_memphysical(card->tankmem); +#endif + return; +} + +/* Driver initialization routine */ +static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct emu10k1_card *card; + + if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "emu10k1: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(struct emu10k1_card)); + +#if LINUX_VERSION_CODE > 0x020320 + if (!pci_dma_supported(pci_dev, EMU10K1_DMA_MASK)) { + printk(KERN_ERR "emu10k1: architecture does not support 32bit PCI busmaster DMA\n"); + kfree(card); + return -ENODEV; + } + + if (pci_enable_device(pci_dev)) { + printk(KERN_ERR "emu10k1: couldn't enable device\n"); + kfree(card); + return -ENODEV; + } + + pci_set_master(pci_dev); + + card->iobase = pci_dev->resource[0].start; + + if (request_region(card->iobase, EMU10K1_EXTENT, card_names[pci_id->driver_data]) == NULL) { + printk(KERN_ERR "emu10k1: IO space in use\n"); + kfree(card); + return -ENODEV; + } + pci_dev->driver_data = card; + pci_dev->dma_mask = EMU10K1_DMA_MASK; +#else + pci_set_master(pci_dev); + + card->iobase = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + + if (check_region(card->iobase, EMU10K1_EXTENT)) { + printk(KERN_ERR "emu10k1: IO space in use\n"); + kfree(card); + return -ENODEV; + } + + request_region(card->iobase, EMU10K1_EXTENT, card_names[pci_id->driver_data]); +#endif + card->irq = pci_dev->irq; + card->pci_dev = pci_dev; + + /* Reserve IRQ Line */ + if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "emu10k1: IRQ in use\n"); + goto err_irq; + } + + pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev); + + printk(KERN_INFO "emu10k1: %s rev %d found at IO 0x%04lx, IRQ %d\n", card_names[pci_id->driver_data], card->chiprev, card->iobase, card->irq); + + spin_lock_init(&card->lock); + card->mixeraddx = card->iobase + AC97DATA; + init_MUTEX(&card->open_sem); + card->open_mode = 0; + init_waitqueue_head(&card->open_wait); + + /* Register devices */ + if ((card->audio1_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) { + printk(KERN_ERR "emu10k1: cannot register first audio device!\n"); + goto err_dev0; + } + + if ((card->audio2_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) { + printk(KERN_ERR "emu10k1: cannot register second audio device!\n"); + goto err_dev1; + } + + if ((card->mixer_num = register_sound_mixer(&emu10k1_mixer_fops, -1)) < 0) { + printk(KERN_ERR "emu10k1: cannot register mixer device!\n"); + goto err_dev2; + } + + if ((card->midi_num = register_sound_midi(&emu10k1_midi_fops, -1)) < 0) { + printk(KERN_ERR "emu10k1: cannot register midi device!\n"); + goto err_dev3; + } + + if (emu10k1_init(card) != CTSTATUS_SUCCESS) { + printk(KERN_ERR "emu10k1: cannot initialize device!\n"); + goto err_emu10k1_init; + } + + if (audio_init(card) != CTSTATUS_SUCCESS) { + printk(KERN_ERR "emu10k1: cannot initialize audio!\n"); + goto err_audio_init; + } + + if (midi_init(card) != CTSTATUS_SUCCESS) { + printk(KERN_ERR "emu10k1: cannot initialize midi!\n"); + goto err_midi_init; + } + + mixer_init(card); + + DPD(2, "Hardware initialized. TRAM allocated: %u bytes\n", (unsigned int) card->tmemsize); + + list_add(&card->list, &emu10k1_devs); + + return 0; + + err_midi_init: + audio_exit(card); + + err_audio_init: + emu10k1_exit(card); + + err_emu10k1_init: + unregister_sound_midi(card->midi_num); + + err_dev3: + unregister_sound_mixer(card->mixer_num); + + err_dev2: + unregister_sound_dsp(card->audio2_num); + + err_dev1: + unregister_sound_dsp(card->audio1_num); + + err_dev0: + free_irq(card->irq, card); + + err_irq: + release_region(card->iobase, EMU10K1_EXTENT); + kfree(card); + + return -ENODEV; +} + +static void __devexit emu10k1_remove(struct pci_dev *pci_dev) +{ +#if LINUX_VERSION_CODE > 0x020320 + struct emu10k1_card *card = pci_dev->driver_data; +#else + struct emu10k1_card *card = list_entry(emu10k1_devs.next, struct emu10k1_card, list); +#endif + midi_exit(card); + audio_exit(card); + emu10k1_exit(card); + + unregister_sound_midi(card->midi_num); + unregister_sound_mixer(card->mixer_num); + unregister_sound_dsp(card->audio2_num); + unregister_sound_dsp(card->audio1_num); + + free_irq(card->irq, card); + release_region(card->iobase, EMU10K1_EXTENT); + + list_del(&card->list); + + kfree(card); + return; +} + +MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@opensource.creative.com)"); +MODULE_DESCRIPTION("Creative EMU10K1 PCI Audio Driver v" DRIVER_VERSION "\nCopyright (C) 1999 Creative Technology Ltd."); + +static struct pci_driver emu10k1_pci_driver __initdata = { + name:"emu10k1", + id_table:emu10k1_pci_tbl, + probe:emu10k1_probe, + remove:emu10k1_remove, +}; + +#if LINUX_VERSION_CODE > 0x020320 +static int __init emu10k1_init_module(void) +{ + printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + return pci_module_init(&emu10k1_pci_driver); +} + +static void __exit emu10k1_cleanup_module(void) +{ + pci_unregister_driver(&emu10k1_pci_driver); + return; +} + +#else + +static int __init emu10k1_init_module(void) +{ + struct pci_dev *dev = NULL; + const struct pci_device_id *pci_id = emu10k1_pci_driver.id_table; + + printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_present()) + return -ENODEV; + + while (pci_id->vendor) { + while ((dev = pci_find_device(pci_id->vendor, pci_id->device, dev))) + emu10k1_probe(dev, pci_id); + + pci_id++; + } + return 0; +} + +static void __exit emu10k1_cleanup_module(void) +{ + struct emu10k1_card *card; + + while (!list_empty(&emu10k1_devs)) { + card = list_entry(emu10k1_devs.next, struct emu10k1_card, list); + + emu10k1_remove(card->pci_dev); + } + + return; +} + +#endif + +module_init(emu10k1_init_module); +module_exit(emu10k1_cleanup_module); diff --git a/drivers/sound/emu10k1/midi.c b/drivers/sound/emu10k1/midi.c new file mode 100644 index 000000000..497e48113 --- /dev/null +++ b/drivers/sound/emu10k1/midi.c @@ -0,0 +1,446 @@ + +/* + ********************************************************************** + * midi.c - /dev/midi interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + ********************************************************************** + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <asm/uaccess.h> + +#include "hwaccess.h" +#include "cardmo.h" +#include "cardmi.h" +#include "midi.h" + +static spinlock_t midi_spinlock = SPIN_LOCK_UNLOCKED; + +static void init_midi_hdr(struct midi_hdr *midihdr) +{ + midihdr->bufferlength = MIDIIN_BUFLEN; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; +} + +static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr) +{ + struct midi_hdr *midihdr; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr *), GFP_KERNEL)) == NULL) { + ERROR(); + return -EINVAL; + } + + init_midi_hdr(midihdr); + + if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return CTSTATUS_ERROR; + } + + if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) != CTSTATUS_SUCCESS) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + return CTSTATUS_ERROR; + } + + *midihdrptr = midihdr; + list_add_tail(&midihdr->list, &midi_dev->mid_hdrs); + + return CTSTATUS_SUCCESS; +} + +static int emu10k1_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct emu10k1_card *card; + struct emu10k1_mididevice *midi_dev; + struct list_head *entry; + + DPF(2, "emu10k1_midi_open()\n"); + + /* Check for correct device to open */ + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (card->midi_num == minor) + break; + } + + if (entry == &emu10k1_devs) + return -ENODEV; + + MOD_INC_USE_COUNT; + + /* Wait for device to become free */ + down(&card->open_sem); + while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&card->open_sem); + MOD_DEC_USE_COUNT; + return -EBUSY; + } + + up(&card->open_sem); + interruptible_sleep_on(&card->open_wait); + + if (signal_pending(current)) { + MOD_DEC_USE_COUNT; + return -ERESTARTSYS; + } + + down(&card->open_sem); + } + + if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) { + MOD_DEC_USE_COUNT; + return -EINVAL; + } + + midi_dev->card = card; + midi_dev->mistate = MIDIIN_STATE_STOPPED; + init_waitqueue_head(&midi_dev->oWait); + init_waitqueue_head(&midi_dev->iWait); + midi_dev->ird = 0; + midi_dev->iwr = 0; + midi_dev->icnt = 0; + INIT_LIST_HEAD(&midi_dev->mid_hdrs); + + if (file->f_mode & FMODE_READ) { + struct midi_openinfo dsCardMidiOpenInfo; + struct midi_hdr *midihdr1; + struct midi_hdr *midihdr2; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) + != CTSTATUS_SUCCESS) { + ERROR(); + kfree(midi_dev); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + /* Add two buffers to receive sysex buffer */ + if (midiin_add_buffer(midi_dev, &midihdr1) != CTSTATUS_SUCCESS) { + kfree(midi_dev); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + if (midiin_add_buffer(midi_dev, &midihdr2) != CTSTATUS_SUCCESS) { + list_del(&midihdr1->list); + kfree(midihdr1->data); + kfree(midihdr1); + kfree(midi_dev); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + } + + if (file->f_mode & FMODE_WRITE) { + struct midi_openinfo dsCardMidiOpenInfo; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) + != CTSTATUS_SUCCESS) { + ERROR(); + kfree(midi_dev); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + } + + file->private_data = (void *) midi_dev; + + card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + + up(&card->open_sem); + + return 0; +} + +static int emu10k1_midi_release(struct inode *inode, struct file *file) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + struct emu10k1_card *card = midi_dev->card; + + DPF(2, "emu10k1_midi_release()\n"); + + if (file->f_mode & FMODE_WRITE) { + if (!(file->f_flags & O_NONBLOCK)) { + + while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) { + DPF(4, "Cannot close - buffers not empty\n"); + + interruptible_sleep_on(&midi_dev->oWait); + + } + } + + emu10k1_mpuout_close(card); + } + + if (file->f_mode & FMODE_READ) { + struct midi_hdr *midihdr; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) { + emu10k1_mpuin_stop(card); + midi_dev->mistate = MIDIIN_STATE_STOPPED; + } + + emu10k1_mpuin_reset(card); + emu10k1_mpuin_close(card); + + while (!list_empty(&midi_dev->mid_hdrs)) { + midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list); + + list_del(midi_dev->mid_hdrs.next); + kfree(midihdr->data); + kfree(midihdr); + } + } + + kfree(midi_dev); + + down(&card->open_sem); + card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE)); + up(&card->open_sem); + wake_up_interruptible(&card->open_wait); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static ssize_t emu10k1_midi_read(struct file *file, char *buffer, size_t count, loff_t * pos) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + ssize_t ret = 0; + u16 cnt; + unsigned long flags; + + DPD(4, "emu10k1_midi_read(), count %x\n", (u32) count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (midi_dev->mistate == MIDIIN_STATE_STOPPED) { + if (emu10k1_mpuin_start(midi_dev->card) + != CTSTATUS_SUCCESS) { + ERROR(); + return -EINVAL; + } + + midi_dev->mistate = MIDIIN_STATE_STARTED; + } + + while (count > 0) { + cnt = MIDIIN_BUFLEN - midi_dev->ird; + + spin_lock_irqsave(&midi_spinlock, flags); + + if (midi_dev->icnt < cnt) + cnt = midi_dev->icnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + DPF(2, " Go to sleep...\n"); + + interruptible_sleep_on(&midi_dev->iWait); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + + continue; + } + + if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) { + ERROR(); + return ret ? ret : -EFAULT; + } + + midi_dev->ird += cnt; + midi_dev->ird %= MIDIIN_BUFLEN; + + spin_lock_irqsave(&midi_spinlock, flags); + + midi_dev->icnt -= cnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if (midi_dev->icnt == 0) + break; + } + + return ret; +} + +static ssize_t emu10k1_midi_write(struct file *file, const char *buffer, size_t count, loff_t * pos) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; + struct midi_hdr *midihdr; + ssize_t ret = 0; + unsigned long flags; + + DPD(4, "emu10k1_midi_write(), count=%x\n", (u32) count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr *), GFP_KERNEL)) == NULL) + return -EINVAL; + + midihdr->bufferlength = count; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + + if ((midihdr->data = (u8 *) kmalloc(count, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return -EINVAL; + } + + if (copy_from_user(midihdr->data, buffer, count)) { + kfree(midihdr->data); + kfree(midihdr); + return ret ? ret : -EFAULT; + } + + spin_lock_irqsave(&midi_spinlock, flags); + + if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) != CTSTATUS_SUCCESS) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + spin_unlock_irqrestore(&midi_spinlock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return count; +} + +static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + DPF(4, "emu10k1_midi_poll() called\n"); + return 0; +} + +int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg) +{ + struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata; + struct midi_hdr *midihdr = NULL; + unsigned long flags; + int i; + + DPF(4, "emu10k1_midi_callback()\n"); + + spin_lock_irqsave(&midi_spinlock, flags); + + switch (msg) { + case ICARDMIDI_OUTLONGDATA: + midihdr = (struct midi_hdr *) pmsg[2]; + + kfree(midihdr->data); + kfree(midihdr); + wake_up_interruptible(&midi_dev->oWait); + + break; + + case ICARDMIDI_INLONGDATA: + midihdr = (struct midi_hdr *) pmsg[2]; + + for (i = 0; i < midihdr->bytesrecorded; i++) { + midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += midihdr->bytesrecorded; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) { + init_midi_hdr(midihdr); + emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr); + wake_up_interruptible(&midi_dev->iWait); + } + break; + + case ICARDMIDI_INDATA: + { + u8 *pBuf = (u8 *) & pmsg[1]; + u16 bytesvalid = pmsg[2]; + + for (i = 0; i < bytesvalid; i++) { + midi_dev->iBuf[midi_dev->iwr++] = pBuf[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += bytesvalid; + } + + wake_up_interruptible(&midi_dev->iWait); + break; + + default: /* Unknown message */ + return CTSTATUS_ERROR; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return CTSTATUS_SUCCESS; +} + +/* MIDI file operations */ +struct file_operations emu10k1_midi_fops = { + read:emu10k1_midi_read, + write:emu10k1_midi_write, + poll:emu10k1_midi_poll, + open:emu10k1_midi_open, + release:emu10k1_midi_release, +}; diff --git a/drivers/sound/emu10k1/midi.h b/drivers/sound/emu10k1/midi.h new file mode 100644 index 000000000..360ca978f --- /dev/null +++ b/drivers/sound/emu10k1/midi.h @@ -0,0 +1,55 @@ +/* + ********************************************************************** + * midi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _MIDI_H +#define _MIDI_H + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define MIDIIN_STATE_STARTED 0x00000001 +#define MIDIIN_STATE_STOPPED 0x00000002 + +#define MIDIIN_BUFLEN 1024 + +struct emu10k1_mididevice +{ + struct emu10k1_card *card; + u32 mistate; + wait_queue_head_t oWait; + wait_queue_head_t iWait; + s8 iBuf[MIDIIN_BUFLEN]; + u16 ird, iwr, icnt; + struct list_head mid_hdrs; +}; + +#endif /* _MIDI_H */ diff --git a/drivers/sound/emu10k1/mixer.c b/drivers/sound/emu10k1/mixer.c new file mode 100644 index 000000000..fa54ca435 --- /dev/null +++ b/drivers/sound/emu10k1/mixer.c @@ -0,0 +1,824 @@ + +/* + ********************************************************************** + * mixer.c - /dev/mixer interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + * This program uses some code from es1371.c, Copyright 1998-1999 + * Thomas Sailer + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + ********************************************************************** + */ + +#define __NO_VERSION__ /* Kernel version only defined once */ +#include <linux/module.h> +#include <asm/uaccess.h> + +#include "hwaccess.h" + +#define AC97_PESSIMISTIC +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +#define vol_to_hw_5(swvol) (31 - (((swvol) * 31) / 100)) +#define vol_to_hw_4(swvol) (15 - (((swvol) * 15) / 100)) + +#define vol_to_sw_5(hwvol) (((31 - (hwvol)) * 100) / 31) +#define vol_to_sw_4(hwvol) (((15 - (hwvol)) * 100) / 15) + +#ifdef PRIVATE_PCM_VOLUME +struct sblive_pcm_volume_rec sblive_pcm_volume[MAX_PCM_CHANNELS]; +#endif + +/* --------------------------------------------------------------------- */ + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* Mapping arrays */ +static const unsigned int recsrc[] = { + SOUND_MASK_MIC, + SOUND_MASK_CD, + SOUND_MASK_VIDEO, + SOUND_MASK_LINE1, + SOUND_MASK_LINE, + SOUND_MASK_VOLUME, + SOUND_MASK_OGAIN, /* Used to be PHONEOUT */ + SOUND_MASK_PHONEIN, + SOUND_MASK_TREBLE, + SOUND_MASK_BASS, + SOUND_MASK_MONITOR, + SOUND_MASK_PCM, +}; + +static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = { + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = AC97_LINEINVOLUME, + [SOUND_MIXER_CD] = AC97_CDVOLUME, + [SOUND_MIXER_VIDEO] = AC97_VIDEOVOLUME, + [SOUND_MIXER_LINE1] = AC97_AUXVOLUME, + +/* [SOUND_MIXER_PCM] = AC97_PCMOUTVOLUME, */ + /* 5 bit stereo, setting 6th bit equal to maximum attenuation */ + +/* [SOUND_MIXER_VOLUME] = AC97_MASTERVOLUME, */ + [SOUND_MIXER_PHONEOUT] = AC97_HEADPHONEVOLUME, + /* 5 bit mono, setting 6th bit equal to maximum attenuation */ + [SOUND_MIXER_OGAIN] = AC97_MASTERVOLUMEMONO, + /* 5 bit mono */ + [SOUND_MIXER_PHONEIN] = AC97_PHONEVOLUME, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = AC97_PCBEEPVOLUME, + /* 5 bit mono, 7th bit = preamp */ + [SOUND_MIXER_MIC] = AC97_MICVOLUME, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = AC97_RECORDGAIN, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = AC97_RECORDGAINMIC, + /* test code */ + [SOUND_MIXER_BASS] = AC97_GENERALPUPOSE, + [SOUND_MIXER_TREBLE] = AC97_MASTERTONE, + [SOUND_MIXER_LINE2] = AC97_PCMOUTVOLUME, + [SOUND_MIXER_DIGITAL2] = AC97_MASTERVOLUME +}; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + +#define swab(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00)) + +/* FIXME: mixer_rdch() is broken. */ + +static int mixer_rdch(struct emu10k1_card *card, unsigned int ch, int *arg) +{ + u16 reg; + int j; + int nL, nR; + + switch (ch) { + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + case SOUND_MIXER_VOLUME: + sblive_readac97(card, volreg[ch], ®); + nL = ((~(reg >> 8) & 0x1f) * 100) / 32; + nR = (~(reg & 0x1f) * 100) / 32; + DPD(2, "mixer_rdch: l=%d, r=%d\n", nL, nR); + return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg); + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: + sblive_readac97(card, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~(reg & 0x1f) * 0x64 / 0x20 * 0x101, (int *) arg); + + case SOUND_MIXER_SPEAKER: + sblive_readac97(card, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~((reg >> 1) & 0xf) * 0x64 / 0x10 * 0x101, (int *) arg); + + case SOUND_MIXER_MIC: + sblive_readac97(card, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~(reg & 0x1f) * 0x64 / 0x20 * 0x101 + ((reg & 0x40) ? 0x1e1e : 0), (int *) arg); + + case SOUND_MIXER_RECLEV: + sblive_readac97(card, volreg[ch], ®); + nL = ((~(reg >> 8) & 0x1f) * 100) / 16; + nR = (~(reg & 0x1f) * 100) / 16; + return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg); + + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_BASS: + return put_user(0x0000, (int *) arg); + default: + return -EINVAL; + } +} + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = { + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = 1, + [SOUND_MIXER_CD] = 2, + [SOUND_MIXER_VIDEO] = 3, + [SOUND_MIXER_LINE1] = 4, + [SOUND_MIXER_PCM] = 5, + /* 6 bit stereo */ + [SOUND_MIXER_VOLUME] = 6, + [SOUND_MIXER_PHONEOUT] = 7, + /* 6 bit mono */ + [SOUND_MIXER_OGAIN] = 8, + [SOUND_MIXER_PHONEIN] = 9, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = 10, + /* 6 bit mono + preamp */ + [SOUND_MIXER_MIC] = 11, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = 12, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = 13, + [SOUND_MIXER_TREBLE] = 14, + [SOUND_MIXER_BASS] = 15, + [SOUND_MIXER_LINE2] = 16, + [SOUND_MIXER_LINE3] = 17, + [SOUND_MIXER_DIGITAL1] = 18, + [SOUND_MIXER_DIGITAL2] = 19 +}; + +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 } +}; + +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 void set_bass(struct emu10k1_card *card, int l, int r) +{ + int i; + + l = (l * 40 + 50) / 100; + r = (r * 40 + 50) / 100; + for (i = 0; i < 5; i++) { + sblive_writeptr(card, FXGPREGBASE + 0x70 + (i * 2), 0, bass_table[l][i]); + sblive_writeptr(card, FXGPREGBASE + 0x70 + (i * 2) + 1, 0, bass_table[r][i]); + } +} + +static void set_treble(struct emu10k1_card *card, int l, int r) +{ + int i; + + l = (l * 40 + 50) / 100; + r = (r * 40 + 50) / 100; + for (i = 0; i < 5; i++) { + sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2), 0, treble_table[l][i]); + sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2) + 1, 0, treble_table[r][i]); + } +} + +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 void update_digital(struct emu10k1_card *card) +{ + int i, k, l1, r1, l2, r2, l3, r3, l4, r4; + u64 j; + + i = card->arrwVol[volidx[SOUND_MIXER_VOLUME]]; + l1 = (i & 0xff); + r1 = ((i >> 8) & 0xff); + i = card->arrwVol[volidx[SOUND_MIXER_LINE3]]; + l2 = i & 0xff; + r2 = (i >> 8) & 0xff; + + i = card->arrwVol[volidx[SOUND_MIXER_PCM]]; + l3 = i & 0xff; + r3 = (i >> 8) & 0xff; + + i = card->arrwVol[volidx[SOUND_MIXER_DIGITAL1]]; + l4 = i & 0xff; + r4 = (i >> 8) & 0xff; + + i = (r1 * r2) / 50; + if (r2 > 50) + r2 = 2 * r1 - i; + else { + r2 = r1; + r1 = i; + } + + i = (l1 * l2) / 50; + if (l2 > 50) + l2 = 2 * l1 - i; + else { + l2 = l1; + l1 = i; + } + + for (i = 0; i < 16; i++) { + if (card->digmix[i] != 0x80000000) { + if ((i >= 0) && (i < 4)) + j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r3]) : ((u64) db_table[l1] * (u64) db_table[l3]); + else if ((i == 6) || (i == 7)) + j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r4]) : ((u64) db_table[l1] * (u64) db_table[l4]); + else + j = ((i & 1) ? db_table[r1] : db_table[l1]) << 31; + card->digmix[i] = j >> 31; + sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, card->digmix[i]); + } + } + + for (i = 64; i < 80; i++) { + if (card->digmix[i] != 0x80000000) { + if ((i >= 64) && (i < 68)) + j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r3]) : ((u64) db_table[l2] * (u64) db_table[l3]); + else if ((i == 70) || (i == 71)) + j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r4]) : ((u64) db_table[l2] * (u64) db_table[l4]); + else + j = ((i & 1) ? db_table[r2] : db_table[l2]) << 31; + card->digmix[i] = j >> 31; + sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, card->digmix[i]); + } + } + + for (i = 16; i <= 80; i += 16) { + if (i != 64) { + for (k = 0; k < 4; k++) + if (card->digmix[i + k] != 0x80000000) { + card->digmix[i + k] = db_table[l3]; + sblive_writeptr(card, FXGPREGBASE + 0x10 + i + k, 0, card->digmix[i + k]); + } + if (card->digmix[i + 6] != 0x80000000) { + card->digmix[i + 6] = db_table[l4]; + sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 6, 0, card->digmix[i + 6]); + } + if (card->digmix[i + 7] != 0x80000000) { + card->digmix[i + 7] = db_table[r4]; + sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 7, 0, card->digmix[i + 7]); + } + } + } + +} + +#ifdef PRIVATE_PCM_VOLUME + +/* calc & set attenuation factor for given channel */ +static int set_pcm_attn(struct emu10k1_card *card, int ch, int l) +{ +#ifndef PCMLEVEL +#define PCMLEVEL 140 // almost silence +#endif + int vol = IFATN_ATTENUATION_MASK; // silence + + if (l > 0) + vol = (PCMLEVEL - (l * PCMLEVEL + 50) / 100); + sblive_writeptr(card, IFATN, ch, IFATN_FILTERCUTOFF_MASK | vol); + DPD(2, "SOUND_MIXER_PCM: channel:%d level:%d attn:%d\n", ch, l, vol); + + return vol; +#undef PCMLEVEL +} + +/* update value of local PCM volume level (using channel attenuation) + * + * return 1: in case its local change + * 0: if the current process doesn't have entry in table + * (it means this process have not opened audio (mixer usually) + */ +static int update_pcm_attn(struct emu10k1_card *card, unsigned l1, unsigned r1) +{ + int i; + int mixer = (r1 << 8) | l1; + + for (i = 0; i < MAX_PCM_CHANNELS; i++) { + if (sblive_pcm_volume[i].files == current->files) { + sblive_pcm_volume[i].mixer = mixer; + if (sblive_pcm_volume[i].opened) { + if (sblive_pcm_volume[i].channel_r < NUM_G) { + if (sblive_pcm_volume[i].channel_l < NUM_G) + sblive_pcm_volume[i].attn_l = set_pcm_attn(card, sblive_pcm_volume[i].channel_l, l1); + sblive_pcm_volume[i].attn_r = set_pcm_attn(card, sblive_pcm_volume[i].channel_r, r1); + } else { + // mono voice + if (sblive_pcm_volume[i].channel_l < NUM_G) + sblive_pcm_volume[i].attn_l = + set_pcm_attn(card, sblive_pcm_volume[i].channel_l, (l1 >= r1) ? l1 : r1); + // to correctly handle mono voice here we would need + // to go into stereo mode and move the voice to the right & left + // looks a bit overcomlicated... + } + + } + return 1; + } + } + if (i == MAX_PCM_CHANNELS) + card->arrwVol[volidx[SOUND_MIXER_PCM]] = mixer; + + return 0; +} +#endif + +int emu10k1_mixer_wrch(struct emu10k1_card *card, unsigned int ch, int val) +{ + int i; + unsigned l1, r1; + u16 wval; + + l1 = val & 0xff; + r1 = (val >> 8) & 0xff; + if (l1 > 100) + l1 = 100; + if (r1 > 100) + r1 = 100; + + DPD(4, "emu10k1_mixer_wrch() called: ch=%u, l1=%u, r1=%u\n", ch, l1, r1); + + if (!volidx[ch]) + return -EINVAL; +#ifdef PRIVATE_PCM_VOLUME + if (ch != SOUND_MIXER_PCM) +#endif + card->arrwVol[volidx[ch]] = (r1 << 8) | l1; + + switch (ch) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_DIGITAL1: + case SOUND_MIXER_LINE3: + DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_VOLUME) ? "VOLUME" : (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3"); + update_digital(card); + return 0; + case SOUND_MIXER_PCM: + DPF(4, "SOUND_MIXER_PCM\n"); +#ifdef PRIVATE_PCM_VOLUME + if (update_pcm_attn(card, l1, r1)) + return 0; +#endif + update_digital(card); + return 0; + case SOUND_MIXER_DIGITAL2: + case SOUND_MIXER_LINE2: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + DPD(4, "SOUND_MIXER_%s:\n", + (ch == SOUND_MIXER_LINE1) ? "LINE1" : + (ch == SOUND_MIXER_LINE2) ? "LINE2" : (ch == SOUND_MIXER_LINE) ? "LINE" : (ch == SOUND_MIXER_DIGITAL2) ? "DIGITAL2" : "CD"); + wval = ((((100 - l1) * 32 + 50) / 100) << 8) | (((100 - r1) * 32 + 50) / 100); + if (wval == 0x2020) + wval = 0x8000; + else + wval -= ((wval & 0x2020) / 0x20); + sblive_writeac97(card, volreg[ch], wval); + return 0; + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: + DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_PHONEIN) ? "PHONEIN" : "OGAIN"); + sblive_writeac97(card, volreg[ch], (l1 < 2) ? 0x8000 : ((100 - l1) * 32 + 50) / 100); + return 0; + + case SOUND_MIXER_SPEAKER: + DPF(4, "SOUND_MIXER_SPEAKER:\n"); + sblive_writeac97(card, volreg[ch], (l1 < 4) ? 0x8000 : (((100 - l1) * 16 + 50) / 100) << 1); + return 0; + + case SOUND_MIXER_MIC: + DPF(4, "SOUND_MIXER_MIC:\n"); + i = 0; + if (l1 >= 30) + // 20dB / (34.5dB + 12dB + 20dB) * 100 = 30 + { + l1 -= 30; + i = 0x40; + } + sblive_writeac97(card, volreg[ch], (l1 < 2) ? 0x8000 : ((((70 - l1) * 0x20 + 35) / 70) | i)); + return 0; + + case SOUND_MIXER_RECLEV: + DPF(4, "SOUND_MIXER_RECLEV:\n"); + + wval = (((l1 * 16 + 50) / 100) << 8) | ((r1 * 16 + 50) / 100); + if (wval == 0) + wval = 0x8000; + else { + if (wval & 0xff) + wval--; + if (wval & 0xff00) + wval -= 0x0100; + } + sblive_writeac97(card, volreg[ch], wval); + return 0; + + case SOUND_MIXER_TREBLE: + DPF(4, "SOUND_MIXER_TREBLE:\n"); + set_treble(card, l1, r1); + return 0; + + case SOUND_MIXER_BASS: + DPF(4, "SOUND_MIXER_BASS:\n"); + set_bass(card, l1, r1); + return 0; + + default: + DPF(2, "Got unknown SOUND_MIXER ioctl\n"); + return -EINVAL; + } +} + +static loff_t emu10k1_mixer_llseek(struct file *file, loff_t offset, int origin) +{ + DPF(2, "sblive_mixer_llseek() called\n"); + return -ESPIPE; +} + +/* Mixer file operations */ + +/* FIXME: Do we need spinlocks in here? */ +static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const char id[] = "SBLive"; + static const char name[] = "Creative SBLive"; + int i, val; + struct emu10k1_card *card = (struct emu10k1_card *) file->private_data; + u16 reg; + + switch (cmd) { + + case SOUND_MIXER_INFO:{ + mixer_info info; + + DPF(4, "SOUND_MIXER_INFO\n"); + + strncpy(info.id, id, sizeof(info.id)); + strncpy(info.name, name, sizeof(info.name)); + + info.modify_counter = card->modcnt; + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + break; + case SOUND_OLD_MIXER_INFO:{ + _old_mixer_info info; + + DPF(4, "SOUND_OLD_MIXER_INFO\n"); + + strncpy(info.id, id, sizeof(info.id)); + strncpy(info.name, name, sizeof(info.name)); + + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + break; + + case OSS_GETVERSION: + DPF(4, "OSS_GETVERSION\n"); + return put_user(SOUND_VERSION, (int *) arg); + break; + + case SOUND_MIXER_PRIVATE1: + DPF(4, "SOUND_MIXER_PRIVATE1"); + + if (copy_to_user((void *) arg, card->digmix, sizeof(card->digmix))) + return -EFAULT; + + return 0; + + break; + case SOUND_MIXER_PRIVATE2: + DPF(4, "SOUND_MIXER_PRIVATE2"); + + if (copy_from_user(card->digmix, (void *) arg, sizeof(card->digmix))) + return -EFAULT; + + for (i = 0; i < sizeof(card->digmix) / sizeof(card->digmix[0]); i++) + sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & 0x80000000) ? 0 : card->digmix[i]); + return 0; + + break; + + default: + break; + } + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + DPF(2, "SOUND_MIXER_READ_RECSRC\n"); + sblive_readac97(card, AC97_RECORDSELECT, ®); + return put_user(recsrc[reg & 7], (int *) arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + DPF(4, "SOUND_MIXER_READ_DEVMASK\n"); + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | + SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_PHONEIN | SOUND_MASK_MIC | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | + SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER | + SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | + SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg); + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + DPF(2, "SOUND_MIXER_READ_RECMASK\n"); + return put_user(SOUND_MASK_MIC | SOUND_MASK_CD | + SOUND_MASK_LINE1 | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_OGAIN | + SOUND_MASK_PHONEIN | SOUND_MASK_MONITOR | + SOUND_MASK_PCM, (int *) arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + DPF(2, "SOUND_MIXER_READ_STEREODEVS\n"); + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | + SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | + SOUND_MASK_RECLEV | SOUND_MASK_LINE3 | + SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | + SOUND_MASK_LINE2, (int *) arg); + + case SOUND_MIXER_CAPS: + DPF(2, "SOUND_MIXER_READ_CAPS\n"); + return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); + +#ifdef PRIVATE_PCM_VOLUME + case SOUND_MIXER_PCM: + // needs to be before default: !! + { + int i; + + for (i = 0; i < MAX_PCM_CHANNELS; i++) { + if (sblive_pcm_volume[i].files == current->files) { + return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg); + } + } + } +#endif + default: + i = _IOC_NR(cmd); + DPD(4, "SOUND_MIXER_READ(%d)\n", i); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return mixer_rdch(card, i, (int *) arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + return put_user(card->arrwVol[volidx[i]], (int *) arg); + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } + } + /* End of _IOC_READ */ + if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE)) + return -EINVAL; + + /* _IOC_WRITE */ + card->modcnt++; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + DPF(2, "SOUND_MIXER_WRITE_RECSRC\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s); */ + else if (i > 1) { + sblive_readac97(card, AC97_RECORDSELECT, ®); + val &= ~recsrc[reg & 7]; + } + + for (i = 0; i < 8; i++) { + if (val & recsrc[i]) { + DPD(2, "Selecting record source to be 0x%04x\n", 0x0101 * i); + sblive_writeac97(card, AC97_RECORDSELECT, 0x0101 * i); + return 0; + } + } + return 0; + + default: + i = _IOC_NR(cmd); + DPD(4, "SOUND_MIXER_WRITE(%d)\n", i); + + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + get_user_ret(val, (int *) arg, -EFAULT); + if (emu10k1_mixer_wrch(card, i, val)) + return -EINVAL; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return mixer_rdch(card, i, (int *) arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + return put_user(card->arrwVol[volidx[i]], (int *) arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + + } +} + +static int emu10k1_mixer_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct emu10k1_card *card; + struct list_head *entry; + + DPF(4, "emu10k1_mixer_open()\n"); + + list_for_each(entry, &emu10k1_devs) { + card = list_entry(entry, struct emu10k1_card, list); + + if (card->mixer_num == minor) + break; + } + + if (entry == &emu10k1_devs) + return -ENODEV; + + MOD_INC_USE_COUNT; + + file->private_data = card; + return 0; +} + +static int emu10k1_mixer_release(struct inode *inode, struct file *file) +{ + DPF(3, "emu10k1_mixer_release()\n"); + MOD_DEC_USE_COUNT; + return 0; +} + +struct file_operations emu10k1_mixer_fops = { + llseek:emu10k1_mixer_llseek, + ioctl:emu10k1_mixer_ioctl, + open:emu10k1_mixer_open, + release:emu10k1_mixer_release, +}; diff --git a/drivers/sound/emu10k1/osutils.c b/drivers/sound/emu10k1/osutils.c new file mode 100644 index 000000000..c0f5f63ac --- /dev/null +++ b/drivers/sound/emu10k1/osutils.c @@ -0,0 +1,91 @@ + +/* + ********************************************************************** + * osutils.c - OS Services layer for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +struct memhandle *emu10k1_alloc_memphysical(u32 size) +{ + struct memhandle *handle; + u32 reqpage, order; + + if ((handle = (struct memhandle *) kmalloc(sizeof(struct memhandle), GFP_KERNEL)) == NULL) + return handle; + + DPD(3, "kmalloc: [%p]\n", handle); + + order = 0; + reqpage = size / PAGE_SIZE; + + if (size % PAGE_SIZE) + reqpage++; + + if (reqpage != 0) { + reqpage--; + while (reqpage > 0) { + reqpage >>= 1; + order++; + } + } + + if ((handle->virtaddx = (void *) __get_free_pages(GFP_KERNEL, order)) == NULL) { + kfree(handle); + + DPD(3, "kfree: [%p]\n", handle); + return (void *) NULL; + } + + /* in linux, we can directly access physical address, don't need to do + * phys_to_virt. + * In linux kernel 2.0.36, virt_to_bus does nothing, get_free_pages + * returns physical address. But in kernel 2.2.1 upwards, + * get_free_pages returns virtual address, we need to convert it + * to physical address. Then this physical address can be used to + * program hardware registers. */ + handle->busaddx = virt_to_bus(handle->virtaddx); + handle->order = order; + + DPD(3, "__get_free_pages: [%p] %lx\n", handle->virtaddx, handle->busaddx); + + return handle; +} + +void emu10k1_free_memphysical(struct memhandle *handle) +{ + free_pages((unsigned long) handle->virtaddx, handle->order); + kfree(handle); + + DPD(3, "free_pages: [%p]\n", handle->virtaddx); + DPD(3, "kfree: [%p]\n", handle); + + return; +} diff --git a/drivers/sound/emu10k1/recmgr.c b/drivers/sound/emu10k1/recmgr.c new file mode 100644 index 000000000..18980ce4e --- /dev/null +++ b/drivers/sound/emu10k1/recmgr.c @@ -0,0 +1,139 @@ + +/* + ********************************************************************** + * recmgr.c -- Recording manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "recmgr.h" + +void emu10k1_start_record(struct record *rec_ptr) +{ + struct emu10k1_card *hw_ptr = rec_ptr->card; + + DPF(2, "emu10k1_start_record()\n"); + DPD(2, "bus addx: %lx\n", rec_ptr->busaddx); + + sblive_writeptr(hw_ptr, rec_ptr->bufaddrreg, 0, rec_ptr->busaddx); + sblive_writeptr(hw_ptr, rec_ptr->bufsizereg, 0, rec_ptr->bufsize); + + if (rec_ptr->adcctl) + sblive_writeptr(hw_ptr, ADCCR, 0, rec_ptr->adcctl); + + return; +} + +void emu10k1_stop_record(struct record *rec_ptr) +{ + struct emu10k1_card *hw_ptr = rec_ptr->card; + + DPF(2, "emu10k1_stop_record()\n"); + + /* Disable record transfer */ + if (rec_ptr->adcctl) + sblive_writeptr(hw_ptr, ADCCR, 0, 0); + + sblive_writeptr(hw_ptr, rec_ptr->bufsizereg, 0, ADCBS_BUFSIZE_NONE); + + return; +} + +void emu10k1_set_record_src(struct record *rec_ptr, u8 recsrc) +{ + DPF(2, "emu10k1_set_record_src()\n"); + + switch (recsrc) { + + case WAVERECORD_AC97: + DPF(2, "recording source: AC97\n"); + rec_ptr->bufsizereg = ADCBS; + rec_ptr->bufaddrreg = ADCBA; + rec_ptr->bufidxreg = ADCIDX_IDX; + + switch (rec_ptr->samplingrate) { + case 0xBB80: + rec_ptr->adcctl = ADCCR_SAMPLERATE_48; + break; + case 0xAC44: + rec_ptr->adcctl = ADCCR_SAMPLERATE_44; + break; + case 0x7D00: + rec_ptr->adcctl = ADCCR_SAMPLERATE_32; + break; + case 0x5DC0: + rec_ptr->adcctl = ADCCR_SAMPLERATE_24; + break; + case 0x5622: + rec_ptr->adcctl = ADCCR_SAMPLERATE_22; + break; + case 0x3E80: + rec_ptr->adcctl = ADCCR_SAMPLERATE_16; + break; + case 0x2B11: + rec_ptr->adcctl = ADCCR_SAMPLERATE_11; + break; + case 0x1F40: + rec_ptr->adcctl = ADCCR_SAMPLERATE_8; + break; + default: + break; + } + + rec_ptr->adcctl |= ADCCR_LCHANENABLE; + + if (rec_ptr->is_stereo) + rec_ptr->adcctl |= ADCCR_RCHANENABLE; + + // rec_ptr->fxwc = 0; + + break; + + case WAVERECORD_MIC: + DPF(2, "recording source: MIC\n"); + rec_ptr->bufsizereg = MICBS; + rec_ptr->bufaddrreg = MICBA; + rec_ptr->bufidxreg = MICIDX_IDX; + rec_ptr->adcctl = 0; + // rec_ptr->fxwc = 0; + break; + + case WAVERECORD_FX: + DPF(2, "recording source: FX\n"); + rec_ptr->bufsizereg = FXBS; + rec_ptr->bufaddrreg = FXBA; + rec_ptr->bufidxreg = FXIDX_IDX; + rec_ptr->adcctl = 0; + // rec_ptr->fxwc = 0x000ffff; + break; + default: + break; + } + + return; +} diff --git a/drivers/sound/emu10k1/recmgr.h b/drivers/sound/emu10k1/recmgr.h new file mode 100644 index 000000000..1fbde606b --- /dev/null +++ b/drivers/sound/emu10k1/recmgr.h @@ -0,0 +1,62 @@ +/* + ********************************************************************** + * recmgr.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _RECORDMGR_H +#define _RECORDMGR_H + +struct record +{ + struct emu10k1_card *card; + u8 *recbuffer; + u32 recpos; + int is_stereo; + int is_16bit; + u32 recbufsize; + u32 bufsize; + u32 bufsizereg; + u32 bufaddrreg; + u32 bufidxreg; + u32 adcctl; + unsigned long busaddx; + u32 samplingrate; +}; + +/* Recording resources */ +#define WAVERECORD_AC97 0x01 +#define WAVERECORD_MIC 0x02 +#define WAVERECORD_FX 0x03 + +void emu10k1_start_record(struct record *); +void emu10k1_stop_record(struct record *); +void emu10k1_set_record_src(struct record *, u8); + + +#endif /* _RECORDMGR_H */ diff --git a/drivers/sound/emu10k1/timer.c b/drivers/sound/emu10k1/timer.c new file mode 100644 index 000000000..1614b464d --- /dev/null +++ b/drivers/sound/emu10k1/timer.c @@ -0,0 +1,176 @@ + +/* + ********************************************************************** + * timer.c + * Copyright (C) 1999, 2000 Creative Labs, inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +/* 3/6/2000 Improved support for different timer delays Rui Sousa */ + +/* 4/3/2000 Implemented timer list using list.h Rui Sousa */ + +#include "hwaccess.h" + +/* Try to schedule only once per fragment */ + +void emu10k1_timer_irqhandler(struct emu10k1_card *card) +{ + struct emu_timer *t; + struct list_head *entry; + + spin_lock(&card->timer_lock); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + if (t->active) { + t->count++; + if (t->count == t->count_max) { + t->count = 0; + tasklet_hi_schedule(&t->tasklet); + } + } + } + + spin_unlock(&card->timer_lock); + + return; +} + +struct emu_timer *emu10k1_timer_install(struct emu10k1_card *card, void (*func) (unsigned long), unsigned long data, u32 delay) +{ + struct emu_timer *timer; + struct emu_timer *t; + struct list_head *entry; + unsigned long flags; + + if ((timer = (struct emu_timer *) kmalloc(sizeof(struct emu_timer), GFP_KERNEL)) == NULL) + return timer; + + if (delay < 5) + delay = 5; + + timer->delay = delay; + tasklet_init(&timer->tasklet, func, data); + timer->active = 0; + + spin_lock_irqsave(&card->timer_lock, flags); + + timer->count_max = timer->delay / (card->timer_delay < 1024 ? card->timer_delay : 1024); + timer->count = timer->count_max - 1; + + list_add(&timer->list, &card->timers); + + if (card->timer_delay > delay) { + if (card->timer_delay == TIMER_STOPPED) + emu10k1_irq_enable(card, INTE_INTERVALTIMERENB); + + card->timer_delay = delay; + delay = (delay < 1024 ? delay : 1024); + + WRITE_FN0(card, TIMER_RATE, delay); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + t->count_max = t->delay / delay; + /* don't want to think much, just force scheduling + on the next interrupt */ + t->count = t->count_max - 1; + } + + DPD(2, "timer rate --> %u\n", delay); + } + + spin_unlock_irqrestore(&card->timer_lock, flags); + + return timer; +} + +void emu10k1_timer_uninstall(struct emu10k1_card *card, struct emu_timer *timer) +{ + struct emu_timer *t; + struct list_head *entry; + u32 delay = TIMER_STOPPED; + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + + list_del(&timer->list); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + if (t->delay < delay) + delay = t->delay; + } + + if (card->timer_delay != delay) { + card->timer_delay = delay; + + if (delay == TIMER_STOPPED) + emu10k1_irq_disable(card, INTE_INTERVALTIMERENB); + else { + delay = (delay < 1024 ? delay : 1024); + + WRITE_FN0(card, TIMER_RATE, delay); + + list_for_each(entry, &card->timers) { + t = list_entry(entry, struct emu_timer, list); + + t->count_max = t->delay / delay; + t->count = t->count_max - 1; + } + } + + DPD(2, "timer rate --> %u\n", delay); + } + + spin_unlock_irqrestore(&card->timer_lock, flags); + + tasklet_unlock_wait(&timer->tasklet); + kfree(timer); + + return; +} + +void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + timer->active = 1; + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} + +void emu10k1_timer_disable(struct emu10k1_card *card, struct emu_timer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + timer->active = 0; + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} diff --git a/drivers/sound/emu10k1/timer.h b/drivers/sound/emu10k1/timer.h new file mode 100644 index 000000000..39e1ee2c9 --- /dev/null +++ b/drivers/sound/emu10k1/timer.h @@ -0,0 +1,47 @@ +/* + ********************************************************************** + * timer.h + * Copyright (C) 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + + +#ifndef _TIMER_H +#define _TIMER_H + +struct emu_timer +{ + struct list_head list; + struct tasklet_struct tasklet; + int active; + u32 count; /* current number of interrupts */ + u32 count_max; /* number of interrupts needed to schedule the bh */ + u32 delay; /* timer delay */ +}; + +struct emu_timer *emu10k1_timer_install(struct emu10k1_card *, void (*)(unsigned long), unsigned long, u32); +void emu10k1_timer_uninstall(struct emu10k1_card *, struct emu_timer *); +void emu10k1_timer_enable(struct emu10k1_card *, struct emu_timer *); +void emu10k1_timer_disable(struct emu10k1_card *, struct emu_timer *); + +#define TIMER_STOPPED 0xffffffff + +#endif /* _TIMER_H */ diff --git a/drivers/sound/emu10k1/voicemgr.c b/drivers/sound/emu10k1/voicemgr.c new file mode 100644 index 000000000..de94a453e --- /dev/null +++ b/drivers/sound/emu10k1/voicemgr.c @@ -0,0 +1,349 @@ + +/* + ********************************************************************** + * voicemgr.c - Voice manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +struct emu_voice *emu10k1_voice_alloc(struct voice_manager *voicemgr, struct voice_allocdesc *voiceallocdesc) +{ + struct emu10k1_card *card = voicemgr->card; + struct emu_voice *voice_tmp = voicemgr->voice; + struct emu_voice *voice = NULL; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_alloc()\n"); + + spin_lock_irqsave(&voicemgr->lock, flags); + + if (voiceallocdesc->flags & VOICEMGR_FLAGS_MONO) { + for (i = 0; i < NUM_G; i++) + if (voice_tmp[i].usage == VOICEMGR_USAGE_FREE) { + voice_tmp[i].flags = VOICEMGR_FLAGS_VOICEMASTER | voiceallocdesc->flags; + voice_tmp[i].usage = voiceallocdesc->usage; + voice = &voice_tmp[i]; + break; + } + } else { + for (i = 0; i < NUM_G; i += 2) + if ((voice_tmp[i].usage == VOICEMGR_USAGE_FREE) + && (voice_tmp[i + 1].usage == VOICEMGR_USAGE_FREE)) { + voice_tmp[i].linked_voice = &voice_tmp[i + 1]; + voice_tmp[i].flags = VOICEMGR_FLAGS_VOICEMASTER | voiceallocdesc->flags; + voice_tmp[i].usage = voiceallocdesc->usage; + voice_tmp[i + 1].flags = VOICEMGR_FLAGS_STEREOSLAVE | voiceallocdesc->flags; + voice_tmp[i + 1].usage = voiceallocdesc->usage; + voice = &voice_tmp[i]; + break; + } + } + + spin_unlock_irqrestore(&voicemgr->lock, flags); + + voice_tmp = voice; + + while (voice_tmp != NULL) { + + DPD(2, " voice allocated -> %d\n", voice_tmp->num); + + sblive_writeptr(card, IFATN, voice_tmp->num, 0xffff); + sblive_writeptr(card, DCYSUSV, voice_tmp->num, ENV_OFF); + sblive_writeptr(card, VTFT, voice_tmp->num, 0xffff); + sblive_writeptr(card, PTRX, voice_tmp->num, 0); + + voice_tmp = voice_tmp->linked_voice; + } + + return voice; +} + +void emu10k1_voice_free(struct voice_manager *voicemgr, struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + struct emu_voice *voice_tmp; + unsigned dcysusv; + u32 cra, sample; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_free()\n"); + + voice_tmp = voice; + + while (voice_tmp != NULL) { + + DPD(2, " voice freed -> %d\n", voice_tmp->num); + + sblive_writeptr(card, IFATN, voice_tmp->num, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK); + sblive_writeptr(card, IP, voice_tmp->num, 0); + + dcysusv = sblive_readptr(card, DCYSUSV, voice_tmp->num) & (DCYSUSV_PHASE1_MASK | DCYSUSV_SUSTAINLEVEL_MASK | DCYSUSV_DECAYTIME_MASK); + sblive_writeptr(card, DCYSUSV, voice_tmp->num, dcysusv | ENV_OFF); + + sblive_writeptr(card, VTFT, voice_tmp->num, VTFT_FILTERTARGET_MASK); + sblive_writeptr(card, PTRX_PITCHTARGET, voice_tmp->num, 0); + sblive_writeptr(card, CVCF, voice_tmp->num, CVCF_CURRENTFILTER_MASK); + sblive_writeptr(card, CPF, voice_tmp->num, 0); + + sample = (voice_tmp->flags & VOICEMGR_FLAGS_16BIT) ? 0 : 0x80808080; + cra = sblive_readptr(card, CCR, voice_tmp->num) & CCR_READADDRESS_MASK; + sblive_writeptr(card, CCR, voice_tmp->num, cra); + cra = (cra >> 18) & 0xf; + sblive_writeptr(card, CD0 + cra, voice_tmp->num, sample); + cra = (cra + 0x1) & 0xf; + sblive_writeptr(card, CD0 + cra, voice_tmp->num, sample); + + for (i = 0; i < NUM_FXSENDS; i++) + voice_tmp->sendhandle[i] = 0; + + voice_tmp->flags = 0; + + spin_lock_irqsave(&voicemgr->lock, flags); + voice_tmp->usage = VOICEMGR_USAGE_FREE; + + voice_tmp = voice_tmp->linked_voice; + voice->linked_voice = NULL; + spin_unlock_irqrestore(&voicemgr->lock, flags); + } + + return; +} + +/* Sets up a voices for Wave Playback */ + +void emu10k1_voice_playback_setup(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + u32 sample, cra = 0, start = 0; + + DPF(2, "emu10k1_voice_playback_setup()\n"); + + while (voice != NULL) { + sblive_writeptr(card, DCYSUSV, voice->num, ENV_OFF); + sblive_writeptr(card, VTFT, voice->num, VTFT_FILTERTARGET_MASK); + sblive_writeptr(card, CVCF, voice->num, CVCF_CURRENTFILTER_MASK); + sblive_writeptr(card, FXRT, voice->num, (voice->flags & VOICEMGR_FLAGS_FXRT2) ? 0xd23c0000 : 0xd01c0000); + + /* Stop CA */ + /* Assumption that PT is alreadt 0 so no harm overwriting */ + sblive_writeptr(card, PTRX, voice->num, (voice->params.send_a << 8) | voice->params.send_b); + + if (voice->flags & VOICEMGR_FLAGS_VOICEMASTER) { + if (voice->linked_voice != NULL) { + /* Set stereo bit */ + cra = 64; + sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK); + sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK); + } else { + cra = 32; + sblive_writeptr(card, CPF, voice->num, 0); + } + + if (voice->flags & VOICEMGR_FLAGS_16BIT) + sample = 0; + else { + cra = cra * 2; + sample = 0x80808080; + } + cra -= 4; + + if (voice->linked_voice != NULL) { + /* CCR_READADDRESS_MASK */ + sblive_writeptr(card, CCR, voice->num, 0x3c << 16); + sblive_writeptr(card, CCR, voice->num + 1, cra << 16); + sblive_writeptr(card, CDE, voice->num + 1, sample); + sblive_writeptr(card, CDF, voice->num + 1, sample); + start = voice->params.start + cra / 2; + } else { + sblive_writeptr(card, CCR, voice->num, 0x1c << 16); /* FIXME: Is 0x1c correct? */ + sblive_writeptr(card, CDE, voice->num, sample); + sblive_writeptr(card, CDF, voice->num, sample); + start = voice->params.start + cra; + } + + if (start > voice->params.endloop) { + start -= voice->params.endloop; + + if (voice->linked_voice != NULL) + cra = (cra << 25) | 0x1bc0000 | ((cra - start) << 9); + else + cra = (cra << 25) | 0x11c0000 | ((cra - start) << 9); + + start += voice->params.startloop; + + if (start >= voice->params.endloop) + start = voice->params.endloop - 1; + } else if (voice->linked_voice != NULL) + cra = (cra << 25) | (0x3c << 16); + else + cra = (cra << 25) | (0x1c << 16); + + start |= CCCA_INTERPROM_0; + } + + /* CSL, ST, CA */ + sblive_writeptr(card, DSL, voice->num, voice->params.endloop | (voice->params.send_d << 24)); + sblive_writeptr(card, PSST, voice->num, voice->params.startloop | (voice->params.send_c << 24)); + + if (voice->flags & VOICEMGR_FLAGS_16BIT) + sblive_writeptr(card, CCCA, voice->num, start); + else + sblive_writeptr(card, CCCA, voice->num, start | CCCA_8BITSELECT); + + /* Clear filter delay memory */ + sblive_writeptr(card, Z1, voice->num, 0); + sblive_writeptr(card, Z2, voice->num, 0); + + /* Invalidate maps */ + sblive_writeptr(card, MAPA, voice->num, MAP_PTI_MASK | (card->silentpage->busaddx * 2)); + sblive_writeptr(card, MAPB, voice->num, MAP_PTI_MASK | (card->silentpage->busaddx * 2)); + + /* Fill cache */ + if (voice->flags & VOICEMGR_FLAGS_VOICEMASTER) + sblive_writeptr(card, CCR, voice->num, cra); + + sblive_writeptr(card, ATKHLDV, voice->num, ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); + sblive_writeptr(card, LFOVAL1, voice->num, 0x8000); + sblive_writeptr(card, ATKHLDM, voice->num, 0); + sblive_writeptr(card, DCYSUSM, voice->num, DCYSUSM_DECAYTIME_MASK); + sblive_writeptr(card, LFOVAL2, voice->num, 0x8000); + sblive_writeptr(card, IP, voice->num, voice->params.initial_pitch); + sblive_writeptr(card, PEFE, voice->num, 0x7f); + sblive_writeptr(card, FMMOD, voice->num, 0); + sblive_writeptr(card, TREMFRQ, voice->num, 0); + sblive_writeptr(card, FM2FRQ2, voice->num, 0); + sblive_writeptr(card, ENVVAL, voice->num, 0xbfff); + sblive_writeptr(card, ENVVOL, voice->num, 0xbfff); + +#ifdef PRIVATE_PCM_VOLUME + { + int i; + + for (i = 0; i < MAX_PCM_CHANNELS; i++) { + if (sblive_pcm_volume[i].channel_l == voice->num) { + voice->params.initial_attn = (sblive_pcm_volume[i].channel_r < NUM_G) ? sblive_pcm_volume[i].attn_l : + // test for mono channel (reverse logic is correct here!) + (sblive_pcm_volume[i].attn_r > + sblive_pcm_volume[i].attn_l) ? sblive_pcm_volume[i].attn_l : sblive_pcm_volume[i].attn_r; + DPD(2, "set left volume %d\n", voice->params.initial_attn); + break; + } else if (sblive_pcm_volume[i].channel_r == voice->num) { + voice->params.initial_attn = sblive_pcm_volume[i].attn_r; + DPD(2, "set right volume %d\n", voice->params.initial_attn); + break; + } + } + } +#endif + sblive_writeptr(card, IFATN, voice->num, IFATN_FILTERCUTOFF_MASK | voice->params.initial_attn); + + voice->params.FC_target = 0xffff; + voice->params.pitch_target = (u16) (IP_TO_CP(voice->params.initial_pitch) >> 16); + + voice = voice->linked_voice; + } + + return; +} + +void emu10k1_voice_start(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + + DPF(2, "emu10k1_voice_start()\n"); + + while (voice != NULL) { + sblive_writeptr(card, PTRX_PITCHTARGET, voice->num, voice->params.pitch_target); + + if (voice->flags & VOICEMGR_FLAGS_VOICEMASTER) + sblive_writeptr(card, CPF_CURRENTPITCH, voice->num, voice->params.pitch_target); + + sblive_writeptr(card, VTFT, voice->num, ((u32) voice->params.volume_target << 16) + | voice->params.FC_target); + sblive_writeptr(card, CVCF, voice->num, ((u32) voice->params.volume_target << 16) + | voice->params.FC_target); + sblive_writeptr(card, DCYSUSV, voice->num, (voice->params.byampl_env_sustain << 8) + | ENV_ON | voice->params.byampl_env_decay); + + /* Using StopOnLoop for MIDI stops the playback + too early, which may cause a DC level to be played + until the note is released. */ + + if (voice->usage == VOICEMGR_USAGE_MIDI) + emu10k1_clear_stop_on_loop(card, voice->num); + else { + if (voice->params.startloop > voice->params.end) + emu10k1_set_stop_on_loop(card, voice->num); + else + emu10k1_clear_stop_on_loop(card, voice->num); + } + voice = voice->linked_voice; + } + + return; +} + +void emu10k1_voice_stop(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + + DPF(2, "emu10k1_voice_stop()\n"); + + while (voice != NULL) { + sblive_writeptr(card, IFATN, voice->num, 0xffff); + sblive_writeptr(card, IP, voice->num, 0); + sblive_writeptr(card, VTFT, voice->num, 0xffff); + sblive_writeptr(card, PTRX_PITCHTARGET, voice->num, 0); + voice = voice->linked_voice; + } + + return; +} + +void emu10k1_voice_setcontrol(struct emu_voice *voice, struct voice_cntlset *setting, u32 numparam) +{ + struct emu10k1_card *card = voice->card; + int count; + + for (count = 0; count < numparam; count++) + sblive_writeptr(card, setting[count].paramID, voice->num, setting[count].value); + + return; +} + +void emu10k1_voice_getcontrol(struct emu_voice *voice, u32 controlid, u32 * value) +{ + struct emu10k1_card *card = voice->card; + + *value = sblive_readptr(card, controlid, voice->num); + + return; +} diff --git a/drivers/sound/emu10k1/voicemgr.h b/drivers/sound/emu10k1/voicemgr.h new file mode 100644 index 000000000..bef340d8b --- /dev/null +++ b/drivers/sound/emu10k1/voicemgr.h @@ -0,0 +1,150 @@ +/* + ********************************************************************** + * sblive_voice.h -- EMU Voice Resource Manager header file + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _VOICEMGR_H +#define _VOICEMGR_H +/* struct emu_voice.usage flags */ +#define VOICEMGR_USAGE_FREE 0x00000000 +#define VOICEMGR_USAGE_MIDI 0x00000001 +#define VOICEMGR_USAGE_PLAYBACK 0x00000002 + +/* struct emu_voice.flags flags */ +#define VOICEMGR_FLAGS_MONO 0x00000002 +#define VOICEMGR_FLAGS_16BIT 0x00000004 +#define VOICEMGR_FLAGS_STEREOSLAVE 0x00000008 +#define VOICEMGR_FLAGS_VOICEMASTER 0x80000000 +#define VOICEMGR_FLAGS_FXRT2 0x00000010 + +struct voice_param +{ + /* Sound engine */ + u32 start; + u32 startloop; + u32 endloop; + u32 end; + + u16 current_pitch; + u16 pitch_target; + + u16 current_volume; + u16 volume_target; + + u16 current_FC; + u16 FC_target; + + u8 pan_target; + u8 aux_target; + + /* FX bus amount send */ + + u32 send_a; + u32 send_b; + u32 send_c; + u32 send_d; + + /* Envelope engine */ + u16 ampl_env_delay; + u8 byampl_env_attack; + u8 byampl_env_hold; + u8 byampl_env_decay; + u8 byampl_env_sustain; + u8 byampl_env_release; + + u16 aux_env_delay; + u8 byaux_env_attack; + u8 byaux_env_hold; + u8 byaux_env_decay; + u8 byaux_env_sustain; + u8 byaux_env_release; + + u16 mod_LFO_delay; /* LFO1 */ + u16 vib_LFO_delay; /* LFO2 */ + u8 mod_LFO_freq; /* LFO1 */ + u8 vib_LFO_freq; /* LFO2 */ + + s8 aux_env_to_pitch; + s8 aux_env_to_FC; + s8 mod_LFO_to_pitch; + s8 vib_LFO_to_pitch; + s8 mod_LFO_to_FC; + s8 mod_LFO_to_volume; + + u16 sample_pitch; + u16 initial_pitch; + u8 initial_attn; + u8 initial_FC; +}; + +struct voice_allocdesc +{ + u32 usage; /* playback, Midi */ + u32 flags; /* stereo/mono rec/playback 8/16 bit*/ +}; + +struct emu_voice +{ + struct list_head list; + + struct emu10k1_card *card; + u32 usage; /* Free, MIDI, playback */ + u32 num; /* Voice ID */ + u32 flags; /* Stereo/mono, rec/playback, 8/16 bit */ + + struct voice_param params; + + struct emu_voice *linked_voice; /*for stereo voice*/ + + u32 sendhandle[NUM_FXSENDS]; +}; + +struct voice_manager +{ + struct emu10k1_card *card; + spinlock_t lock; + + struct emu_voice voice[NUM_G]; +}; + +struct voice_cntlset +{ + u32 paramID; + u32 value; +}; + +struct emu_voice *emu10k1_voice_alloc(struct voice_manager *, struct voice_allocdesc *); +void emu10k1_voice_free(struct voice_manager *, struct emu_voice *); +void emu10k1_voice_playback_setup(struct emu_voice *); +void emu10k1_voice_start(struct emu_voice *); +void emu10k1_voice_stop(struct emu_voice *); +void emu10k1_voice_setcontrol(struct emu_voice *, struct voice_cntlset *, u32); +void emu10k1_voice_getcontrol(struct emu_voice *, u32, u32 *); + +#endif /* _VOICEMGR_H */ |