summaryrefslogtreecommitdiffstats
path: root/drivers/sound/emu10k1
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sound/emu10k1')
-rw-r--r--drivers/sound/emu10k1/8010.h662
-rw-r--r--drivers/sound/emu10k1/Makefile29
-rw-r--r--drivers/sound/emu10k1/audio.c1441
-rw-r--r--drivers/sound/emu10k1/audio.h46
-rw-r--r--drivers/sound/emu10k1/cardmi.c813
-rw-r--r--drivers/sound/emu10k1/cardmi.h113
-rw-r--r--drivers/sound/emu10k1/cardmo.c229
-rw-r--r--drivers/sound/emu10k1/cardmo.h61
-rw-r--r--drivers/sound/emu10k1/cardwi.c472
-rw-r--r--drivers/sound/emu10k1/cardwi.h92
-rw-r--r--drivers/sound/emu10k1/cardwo.c755
-rw-r--r--drivers/sound/emu10k1/cardwo.h118
-rw-r--r--drivers/sound/emu10k1/efxmgr.c34
-rw-r--r--drivers/sound/emu10k1/efxmgr.h43
-rw-r--r--drivers/sound/emu10k1/emu_wrapper.h11
-rw-r--r--drivers/sound/emu10k1/emuadxmg.c101
-rw-r--r--drivers/sound/emu10k1/hwaccess.c430
-rw-r--r--drivers/sound/emu10k1/hwaccess.h204
-rw-r--r--drivers/sound/emu10k1/icardmid.h163
-rw-r--r--drivers/sound/emu10k1/icardwav.h52
-rw-r--r--drivers/sound/emu10k1/irqmgr.c127
-rw-r--r--drivers/sound/emu10k1/irqmgr.h59
-rw-r--r--drivers/sound/emu10k1/main.c825
-rw-r--r--drivers/sound/emu10k1/midi.c446
-rw-r--r--drivers/sound/emu10k1/midi.h55
-rw-r--r--drivers/sound/emu10k1/mixer.c824
-rw-r--r--drivers/sound/emu10k1/osutils.c91
-rw-r--r--drivers/sound/emu10k1/recmgr.c139
-rw-r--r--drivers/sound/emu10k1/recmgr.h62
-rw-r--r--drivers/sound/emu10k1/timer.c176
-rw-r--r--drivers/sound/emu10k1/timer.h47
-rw-r--r--drivers/sound/emu10k1/voicemgr.c349
-rw-r--r--drivers/sound/emu10k1/voicemgr.h150
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, &reg);
+ DPD(2, "RESET 0x%x\n", reg);
+ sblive_readac97(card, AC97_MASTERTONE, &reg);
+ 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], &reg);
+ 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], &reg);
+ return put_user(reg & 0x8000 ? 0 : ~(reg & 0x1f) * 0x64 / 0x20 * 0x101, (int *) arg);
+
+ case SOUND_MIXER_SPEAKER:
+ sblive_readac97(card, volreg[ch], &reg);
+ return put_user(reg & 0x8000 ? 0 : ~((reg >> 1) & 0xf) * 0x64 / 0x10 * 0x101, (int *) arg);
+
+ case SOUND_MIXER_MIC:
+ sblive_readac97(card, volreg[ch], &reg);
+ 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], &reg);
+ 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, &reg);
+ 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, &reg);
+ 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 */